2015-09-09 15 views
11

Tôi hiện đang gặp sự cố trong C# mà tôi cho rằng có thể được giải quyết bằng các loại hiện có. Tuy nhiên, tôi không thực sự biết nếu họ có thể được tạo ra trong C#, hoặc mô phỏng (sử dụng một số cấu trúc khác).Các loại tồn tại trong C#?

Về cơ bản tôi muốn có một số mã như thế này:

public interface MyInterface<T> 
{ 
    T GetSomething(); 
    void DoSomething(T something); 
} 

public class MyIntClass : MyInterface<int> 
{ 
    int GetSomething() 
    { 
     return 42; 
    } 

    void DoSomething(int something) 
    { 
     Console.Write(something); 
    } 
} 

public class MyStringClass : MyInterface<string> 
{ 
    string GetSomething() 
    { 
     return "Something"; 
    } 

    void DoSomething(string something) 
    { 
     SomeStaticClass.DoSomethingWithString(something); 
    } 
} 

Tiếp theo, tôi muốn để có thể lặp thông qua một danh sách các đối tượng mà thực hiện giao diện này, nhưng mà không quan tâm những gì tham số kiểu nó có. Một cái gì đó như thế này:

public static void DoALotOfThingsTwice(){ 
    var listOfThings = new List<MyInterface<T>>(){ 
     new MyIntClass(), 
     new MyStringClass(); 
    }; 

    foreach (MyInterface<T> thingDoer in listOfThings){ 
     T something = thingDoer.GetSomething(); 
     thingDoer.DoSomething(something); 
     thingDoer.DoSomething(something); 
    } 
} 

này không biên dịch vì T sử dụng bởi MyIntClass và được sử dụng bởi MyStringClass là khác nhau.

Tôi đã suy nghĩ rằng một cái gì đó như thế này có thể làm các trick, nhưng tôi không biết nếu có một cách hợp lệ để làm như vậy trong C#:

public static void DoALotOfThingsTwice(){ 
    var listOfThings = new List<∃T.MyInterface<T>>(){ 
     new MyIntClass(), 
     new MyStringClass(); 
    }; 

    foreach (∃T.MyInterface<T> thingDoer in listOfThings){ 
     T something = thingDoer.GetSomething(); 
     thingDoer.DoSomething(something); 
     thingDoer.DoSomething(something); 
    } 
} 
+2

Như kiểu là bất biến, không, đó là không thể. – Servy

+2

Bạn có thể bọc hoạt động trong một 'Hành động' và sau đó lưu trữ các hoạt động đó trong một danh sách. Bạn có thể tạo những hành động đó một cách tổng quát. – Lee

Trả lời

5

DoALotOfThingsTwice không phụ thuộc vào số T, bạn có thể bọc nó trong một số Action và lưu trữ những thứ đó trong danh sách thay thế, ví dụ:

public static Action DoSomethingTwice<T>(this MyInterface<T> i) 
{ 
    return() => 
    { 
     T something = i.GetSomething(); 
     i.DoSomething(something); 
     i.DoSomething(something); 
    }; 
} 

sau đó

var listOfThings = new List<Action>() { 
    new MyIntClass().DoSomethingTwice(), 
    new MyStringClass().DoSomethingTwice() 
}; 
+0

Điều này sẽ chỉ hoạt động nếu tôi có thể thực hiện một số công việc bổ sung trong danh sách đó, đúng không? Ví dụ nếu 'DoSomethingTwice' trả về một giá trị, làm cho nó 'Func ' (đối với một số kiểu 'X'), thì nó cho phép tôi hoạt động trên danh sách bằng cách sử dụng các giá trị' X' đó (sau khi đánh giá từng hàm). Nhưng nếu tôi không làm bất cứ điều gì khác ngoài 'DoSomethingTwice', thì sẽ giống như chỉ cần gọi' DoSomethingTwice' trên từng đối tượng riêng biệt, mà không sử dụng danh sách nào cả, đúng không? – gonzaw

+0

@gonzaw - Bạn không thể gọi 'DoSomethingTwice' trên mỗi phần tử riêng biệt trong vòng lặp' foreach' của bạn bởi vì bạn không thể xây dựng danh sách bạn muốn ngay từ đầu. Bạn cần phải xây dựng một số loại wrapper tại điểm mà bạn có thể tóm tắt trên 'T'. Nếu bạn cần trả về giá trị 'T' được sử dụng bên trong vòng lặp' foreach' của bạn thì bạn có cùng một vấn đề, tức là bạn không thể mô tả 'T' vì C# không hỗ trợ kiểu tồn tại như bạn muốn. Tuy nhiên, bạn sẽ làm gì với mỗi 'T' bên trong vòng lặp? Bạn không thể làm bất cứ điều gì một cách có ý nghĩa mà không biết 'T'. – Lee

3

Không thể trực tiếp trong C#.

Bạn có thể an toàn kiểu thả và có giao diện cơ sở không chung chung và sử dụng nó cho mã "chung chung":

public interface MyInterface 
{ 
    object GetSomething(); 
    void DoSomething(object something); 
} 

public interface MyInterface<T> : MyInterface 
{ 
    T GetSomething(); 
    void DoSomething(T something); 
} 

Hoặc sử dụng dynamic (một lần nữa không có thời gian biên dịch loại an toàn):

foreach (dynamic thingDoer in listOfThings) 
{ 
    dynamic something = thingDoer.GetSomething(); 
    thingDoer.DoSomething(something); 
    thingDoer.DoSomething(something); 
} 

Hoặc tạo nhiều phiên bản của trình xử lý và tạo (có thể với bộ nhớ đệm) dựa trên loại (How do I use reflection to call a generic method?) (Lưu ý: bạn không thể thể hiện "danh sách các đối tượng tùy ý" tốt hơn List<object> hoặc List<NonGenericBaseInterface> hoặc List<NonGenericBaseClass>):

foreach (object thingDoer in listOfThings) 
{ 
    // get Do via reflection and create specific version based on 
    // thingDoer.GetType(), than invoke 
    // consider caching "methodForType" in Dictionary by type 
    MethodInfo method = this.GetType().GetMethod("Do"); 
    MethodInfo methodForType = method.MakeGenericMethod(thingDoer.GetType()); 
    methodForType.Invoke(thingDoer, null); 

} 

void Do<T>(MyInterface<T> thingDoer) 
{ 
    T something = thingDoer.GetSomething(); 
    thingDoer.DoSomething(something); 
    thingDoer.DoSomething(something); 
} 

Cách khác để phản ánh là sử dụng cây Biểu thức để tạo mã tương tự.

+0

Cách tiếp cận nào sẽ cho phép tôi tích hợp nó với LINQ? Ví dụ, nếu tôi trích xuất mã "chung" thành một hàm 'public static void DoSomethingTwice (MyInterface thingDoer)', một trong những cách tiếp cận của bạn có cho phép tôi ánh xạ nó bằng cách sử dụng 'listOfthings.Select (DoSomethingTwice)'? Có vẻ như cách tiếp cận bằng cách sử dụng 'đối tượng' sẽ làm việc cho điều này, phải không? – gonzaw

+0

@gonzaw có, giao diện không chung chung sẽ cho phép một số loại an toàn ('Danh sách ') và sử dụng linq 'listOfthings.Select (DoSomethingTwice)', nhưng nó sẽ không gửi đi trên loại phần tử - do đó, 'DoSomethingTwice' sẽ có để làm việc cho tất cả các loại hoặc gửi công văn trên loại mã. –

1

Vì tôi không biết vấn đề cơ bản của bạn trong phạm vi thực tế của bạn là những gì tôi không thể cung cấp một giải pháp chống đạn. Cần phải nỗ lực mà bạn nên xem xét về what's covariance and contravariance in generic parameters both on interfaces and delegates và cung cấp cho phương pháp tiếp cận này một lần thử tái cấu trúc mã của bạn.

Còn bây giờ, tôi tin rằng đây sẽ là một giải pháp khả thi:

public static void DoALotOfThingsTwice() 
{ 
    var listOfThings = new List<object> 
    { 
     new MyIntClass(), new MyStringClass() 
    }; 

    MyInterface<int> a; 
    MyInterface<string> b; 

    // During each iteration, check if the thing is a concrete 
    // implementation of your interface MyInterface<T>... 
    foreach (object thingDoer in listOfThings) 
    {   
     // ...and call MyInterface<T>.DoSomething method depending on 
     // the success of the cast to MyInterface<int> or 
     // MyInterface<string> 
     if ((a = thingDoer as MyInterface<int>) != null) 
      a.DoSomething(38); 
     else if((b = thingDoer as MyInterface<string>) != null) 
      b.DoSomething("hello world"); 
    } 
} 
Các vấn đề liên quan