2015-09-09 5 views
11

У меня в настоящее время проблема с C#, которая, как я думаю, может быть решена с использованием экзистенциальных типов. Тем не менее, я действительно не знаю, могут ли они быть созданы на C# или смоделированы (с использованием какой-либо другой конструкции).Экзистенциальные типы в C#?

В основном я хочу, чтобы иметь некоторый код, как это:

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

Далее я хочу, чтобы иметь возможность перебирать список объектов, которые реализуют этот интерфейс, но, не заботясь о каком типе параметра имеет. Что-то вроде этого:

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

Это не компилируется, потому что T используется MyIntClass и один используется MyStringClass разные.

Я думал, что нечто подобное может сделать трюк, но я не знаю, есть ли действительный способ сделать это в 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

Как тип инвариантно, нет, это невозможно. – Servy

+2

Вы можете перенести операцию в «Действие», а затем сохранить ее в списке. Вы можете создавать эти действия в общих чертах. – Lee

ответ

5

С DoALotOfThingsTwice не зависит от T вы можете обернуть его в Action и сохранить их в списке, а не напр.

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

затем

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

Это будет работать, только если я смогу сделать дополнительную работу над этим списком, верно? Например, если «DoSomethingTwice» вернул значение, сделав его «Func » (для некоторого типа «X»), то это позволило бы мне работать с списком, используя эти значения «X» (после оценки каждой функции). Но если я не сделаю ничего другого, кроме «DoSomethingTwice», тогда было бы одинаково просто вызвать «DoSomethingTwice» для каждого объекта отдельно, не используя список вообще, правильно? – gonzaw

+0

@gonzaw - Ну, вы не можете называть 'DoSomethingTwice' для каждого элемента отдельно в цикле' foreach', потому что вы не можете создать нужный вам список. Вам нужно создать какую-то оболочку в точке, где вы можете абстрагироваться над 'T'. Если вам нужно вернуть значение 'T', которое будет использоваться внутри вашего цикла foreach, тогда у вас будет та же проблема, то есть вы не можете вообще описать' T', так как C# не поддерживает экзистенциальные типы по вашему желанию. Однако, что бы вы сделали с каждым 'T' внутри цикла? Вы не можете ничего значимого сделать, не зная 'T'. – Lee

3

не представляется возможным непосредственно в C#.

Вы можешь безопасность типа падения и иметь не общий базовый интерфейс и использовать его для «общего» кода:

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

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

Или используйте dynamic (опять нет безопасности компиляции типа времени):

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

Или создать несколько версий обработчика и создать (возможно, с кешированием) на основе типа (How do I use reflection to call a generic method?) (Примечание: вы не можете реально выразить «список произвольных объектов» лучше, чем List<object> или List<NonGenericBaseInterface> или):

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

Альтернатива отражения заключается в использовании выражений дерева построить подобный код.

+0

Какой из этих подходов позволит мне интегрировать его с LINQ? Например, если я извлечу «общий» код в функцию «public static void DoSomethingTwice (MyInterface thingDoer)», мог бы один из ваших подходов разрешить сопоставить его с помощью 'listOfthings.Select (DoSomethingTwice)'? Похоже, что подход, использующий 'object', будет работать для этого, не так ли? – gonzaw

+0

@gonzaw yes, не общий интерфейс позволит обеспечить некоторую безопасность типов ('List ') и использовать linq 'listOfthings.Select (DoSomethingTwice)', но он не будет отправлять по типу элемента, поэтому 'DoSomethingTwice' будет иметь работать на всех типах или отправлять по типу в коде. –

1

Так как я не знаю, что ваш основной вопрос в Вашем домене не может обеспечить пуленепробиваемое решение. Это должно стоить усилий, которые вы должны посмотреть примерно what's covariance and contravariance in generic parameters both on interfaces and delegates и дать этот подход попробовать рефакторинг кода.

В настоящем время, я считаю, что это должно быть возможным решением:

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"); 
    } 
} 
Смежные вопросы