2012-04-13 27 views
5

Tôi đã làm việc thông qua một vấn đề trong một vài giờ, và tôi nghĩ rằng tôi gần gũi. Tôi đang làm việc trên một ứng dụng mà chúng tôi có thể có 50-100 loại thực hiện theo cùng một cách. Vì vậy, thay vì tạo 50-100 lớp học, tôi đã cố gắng để làm cho nó chung chung và đây là những gì tôi có:Tạo Kiểu Chung với Giao diện Chung tại Thời gian Chạy

Đây là lớp cơ sở:

public class RavenWriterBase<T> : IRavenWriter<T> where T : class, IDataEntity 

Và đây là giao diện:

public interface IRavenWriter<T> 
{ 
    int ExecutionIntervalInSeconds { get; } 
    void Execute(object stateInfo); 
    void Initialize(int executionIntervalInSeconds, Expression<Func<T, DateTime>> timeOrderByFunc); 
} 

Và đây là cách tôi đang sử dụng nó:

private static void StartWriters() 
{ 
    Assembly assembly = typeof(IDataEntity).Assembly; 
    List<IDataEntity> dataEntities = ReflectionUtility.GetObjectsForAnInterface<IDataEntity>(assembly); 

    foreach (IDataEntity dataEntity in dataEntities) 
    { 
     Type dataEntityType = dataEntity.GetType(); 
     Type ravenWriterType = typeof(RavenWriterBase<>).MakeGenericType(dataEntityType); 

     Expression<Func<IDataEntity, DateTime>> func = x => x.CicReadTime; 

     // This is where I'm stuck. How do I activate this as RavenWriterBase<T>? 
     var ravenWriter = Activator.CreateInstance(ravenWriterType); 

     //ravenWriter.Initialize(60, func); // I can't do this until I cast. 

     // More functionality here (not part of this issue) 
    } 
} 

tôi bị mắc kẹt trên đường dây này từ trên cao:

var ravenWriter = Activator.CreateInstance(ravenWriterType); 

Đây là câu hỏi của tôi:
Làm thế nào tôi có thể sử dụng như RavenWriterBase hoặc IRavenWriter? Một cái gì đó như:

ravenWriter.Initialize(60, func); 

Tôi nghĩ rằng nó cần phải được một cái gì đó như thế này, nhưng tôi cần phải xác định một kiểu cho IRavenWriter <> và tôi không biết nó được nêu ra:

var ravenWriter = Activator.CreateInstance(ravenWriterType) as IRavenWriter<>; 

Nếu tôi di chuột qua ravenWriter, tôi thành công có đối tượng của tôi:

enter image description here

Nhưng bây giờ tôi cần để có thể sử dụng nó trong một cách chung chung. Làm thế nào tôi có thể làm điều đó?

Cập nhật:

tôi chỉ nghĩ đến việc sử dụng từ khóa năng động, và các công trình này:

dynamic ravenWriter = Activator.CreateInstance(ravenWriterType); 
ravenWriter.Initialize(60); 

tôi lừa một chút vì tôi nhận ra rằng Func là như nhau đối với từng IDataEntity, vì vậy không cần thiết để chuyển thành tham số khởi tạo(). Tuy nhiên, ít nhất bây giờ tôi có thể gọi khởi tạo(). Nhưng bây giờ Func là như nhau, tôi cũng không cần giao diện chung.

+2

Nghe có vẻ như sử dụng không đúng các loại thuốc generic. Khi bạn thấy mình cần chạy các phương thức khác nhau dựa trên các kiểu khác nhau, nhưng các kiểu chỉ được biết khi chạy, bạn muốn sử dụng đa hình. Các generics là gì? Bạn có thể chạy các phương thức như là không có generics ở nơi đầu tiên, tức là, với IDataEntity? – mellamokb

+1

Tôi đồng ý với mellamokb. Tuy nhiên, nếu bạn được thiết lập trên generics, tại sao không tạo ra một lớp wrapper mà bạn có thể nhanh chóng non-genarically. Trình bao bọc phải có phương thức trả về một cá thể của RavenWriterBase tùy thuộc vào loại được truyền vào như một đối số. Sau đó, khởi tạo trình bao bọc và gọi phương thức với kiểu được yêu cầu làm tham số. Điều này sẽ đòi hỏi một tuyên bố chuyển đổi lớn, nhưng ít nhất không phải 200 lớp riêng biệt. – dcow

+2

Bạn có tạo các Biểu thức khác không sử dụng IDataEntity cho T không? Hoặc là T luôn luôn là một IDataEntity? Và nếu T luôn là IDataEntity, tại sao lại sử dụng Generics? –

Trả lời

2

Giải pháp của tôi sẽ được:

  • Tạo một giao diện phi chung của IRavenWriter
  • Hãy IRavenWriter<T> kế thừa từ IRavenWriter
  • Giữ ExecuteExecutionIntervalInSeconds trong IRavenWriter
  • Hãy IRavenWriterFunc<DateTime> và sử dụng trong nhà văn của bạn
  • M ove Initialize-IRavenWriter<T>
  • Sử dụng một nhà máy để khởi sự Func theo loại và biểu hiện:

Ví dụ:

public class MyDateTime 
{ 
    public DateTime This { get; set; } 
} 

public static Func<DateTime> GetFunk<T>(Expression<Func<T, DateTime>> timeOrderByFunc, T t) 
{ 
    return() => timeOrderByFunc.Compile()(t); 
} 

Và bạn sử dụng:

GetFunk<MyDateTime>(x => x.This, new MyDateTime(){This = DateTime.Now}); 
+0

Tôi không chắc chắn tôi theo dõi. Điều đó cho phép tôi sử dụng ravenWriter như RavenWriterBase <> hoặc IRavenWriter <> như thế nào? –

+0

@BobHorn nó không. Bạn không thể có kiến ​​thức về 'RavenWriterBase <>' trong đó. Thay vào đó, bạn cần phải sử dụng 'IRavenWriterBase' (không chung chung) trong đó.Kiến thức biên dịch-thời gian của loại không tồn tại trong đó, do đó bạn không thể đúc. – Aliostad

+0

Cảm ơn bạn. Tôi chỉ cần chỉnh sửa câu hỏi của tôi để cho thấy làm thế nào tôi có thể gọi Initialize() bằng cách sử dụng năng động. Tôi nghĩ rằng nên làm việc. –

1

Nó không thực sự khó khăn để biến thời gian chạy Type thành tham số Kiểu chung chung thời gian biên dịch. Chỉ cần giới thiệu giao diện mới để tạo/khởi tạo đối tượng của bạn:

 

interface IRawenWriterFactory 
{ 
    object Create(); 
} 

class RawenWriterFactory<T> : IRawenWriterFactory 
{ 
    public object Create() 
    { 
    Expression<Func<IDataEntity, DateTime>> func = x => x.CicReadTime; 

    var ravenWriter = new RavenWriterBase<T>(); 
    ravenWriter.Initialize(60, func); 

    return ravenWriter; 
    } 
} 
 

Bây giờ chỉ cần tạo RawenWriterFactory với dataEntityType giống như bạn đã tạo ravenWriter và sử dụng nó thông qua giao diện IRavenWriterFactory không chung chung.

Tuy nhiên, có thể có các giải pháp đơn giản hơn nếu bạn thay đổi thiết kế của mình. Ví dụ. nếu bạn bật phương thức Initialize thành hàm tạo, bạn có thể chuyển func thành tham số Activator.CreateInstance và bạn sẽ không cần sử dụng giao diện chung để khởi tạo.

Các vấn đề liên quan