2015-09-09 21 views
11

Obecnie mam do czynienia z problemem w języku C#, który moim zdaniem mógłby zostać rozwiązany przy użyciu typów egzystencjalnych. Jednak tak naprawdę nie wiem, czy można je utworzyć w języku C#, czy symulować (używając innego konstruktu).Egzystencjalne typy w C#?

Zasadniczo chcę mieć pewne kodu:

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); 
    } 
} 

Dalej chcę być w stanie wykonać iterację listy obiektów, które implementują ten interfejs, ale nie dbając jaki typ parametr ma. Coś takiego:

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); 
    } 
} 

nie skompilować, ponieważ T wykorzystywane przez MyIntClass i jeden używany przez MyStringClass są różne.

Myślałam, że coś takiego może załatwić sprawę, ale nie wiem, czy jest poprawny sposób to zrobić w 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

jako typ jest niezmienna, nie, to nie jest możliwe. – Servy

+2

Można zawinąć operację w "Akcji", a następnie zapisać je na liście. Możesz tworzyć te akcje w sposób ogólny. – Lee

Odpowiedz

5

Od DoALotOfThingsTwice nie zależy od T można go opakować w Action i zapisać na liście na liście np.

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

następnie

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

To działałoby tylko wtedy, gdybym mógł wykonać dodatkową pracę na tej liście, prawda? Na przykład, jeśli 'DoSomethingTwice' zwróci wartość, czyniąc ją' Func '(dla pewnego typu' X'), to pozwoli mi operować na liście używając tych wartości 'X' (po ocenie każdej funkcji). Ale jeśli nie zrobię niczego innego niż 'DoSomethingTwice', to byłoby tak samo, aby po prostu wywołać' DoSomethingTwice' na każdym obiekcie osobno, bez korzystania z listy w ogóle, prawda? – gonzaw

+0

@gonzaw - Cóż, nie możesz wywołać 'DoSomethingTwice' na każdym elemencie osobno w pętli' foreach', ponieważ nie możesz zbudować listy, którą chcesz. Musisz skonstruować jakiś rodzaj opakowania w punkcie, w którym możesz streścić 'T'. Jeśli chcesz zwrócić wartość 'T' do użycia w pętli' foreach', to masz ten sam problem, to znaczy nie możesz opisać 'T' w ogóle, ponieważ C# nie obsługuje typów egzystencjalnych, jak chcesz. Co jednak zrobiłbyś z każdym 'T' wewnątrz pętli? Nie możesz sensownie zrobić niczego bez znajomości 'T'. – Lee

3

Niemożliwy bezpośrednio w języku C#.

Można spadek bezpieczeństwa typu i mieć nierodzajową interfejs bazowy i użyć go do „rodzajowe” Kod:

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

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

Albo użyć dynamic (znów brak bezpieczeństwa kompilacji typ czas):

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

Lub wygeneruj wiele wersji programu obsługi i utwórz (prawdopodobnie z buforowaniem) w oparciu o typ (How do I use reflection to call a generic method?) (Uwaga: nie można naprawdę wyrazić "listy dowolnych obiektów" lepiej niż List<object> lub List<NonGenericBaseInterface> lub 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); 
} 

Alternatywą dla refleksji jest użycie drzewa wyrażeń do zbudowania podobnego kodu.

+0

Które z tych podejść pozwoli mi zintegrować go z LINQ? Na przykład, jeśli wyodrębnię kod "ogólny" do funkcji 'public static void DoSomethingTwice (MyInterface thingDoer)', czy jedno z twoich podejść pozwoli mi na mapowanie go za pomocą 'listOfthings.Select (DoSomethingTwice)'? Wygląda na to, że podejście wykorzystujące "obiekt" zadziała, prawda? – gonzaw

+0

@gonzaw tak, nietypowy interfejs pozwoliłby na pewne bezpieczeństwo typów ('ListOfthings.Select (DoSomethingTwice)', ale nie będzie wysyłał na typ elementu - więc 'DoSomethingTwice' miałby do pracy dla wszystkich typów lub do wysyłki według typu w kodzie. –

1

Ponieważ nie wiem, jaka jest twoja kwestia, na aktualnej domeny nie może zapewnić kuloodporną rozwiązanie. Warto wart wysiłku, który powinieneś sprawdzić na temat what's covariance and contravariance in generic parameters both on interfaces and delegates i dać temu podejściu próbę refaktoryzacji kodu.

Na razie uważam, że powinno to być możliwe rozwiązanie:

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"); 
    } 
}