Экзистенциальные типы в С#?
В настоящее время я сталкиваюсь с проблемой на С#, которая, как я думаю, может быть решена с использованием экзистенциальных типов. Тем не менее, я действительно не знаю, могут ли они быть созданы на С# или смоделированы (с использованием какой-либо другой конструкции).
В принципе, я хочу иметь такой код:
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
, отличается.
Я думал, что что-то вроде этого может сделать трюк, но я не знаю, есть ли правильный способ сделать это на С#:
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);
}
}
Ответы
Ответ 1
Так как 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()
};
Ответ 2
Невозможно непосредственно в С#.
Вы можете отказаться от безопасности типа и иметь базовый базовый интерфейс и использовать его для "общего" кода:
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);
}
Или создать несколько версий обработчика и создать (возможно, с кешированием) на основе типа (Как использовать отражение для вызова универсального метода?) (Примечание: что вы не можете выразить "список произвольных объектов" лучше, чем List<object>
или List<NonGenericBaseInterface>
или 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);
}
Альтернативой отражению является использование дерева выражений для создания аналогичного кода.
Ответ 3
Поскольку я не знаю, какова ваша основная проблема в вашем фактическом домене, я не могу предоставить пуленепробиваемое решение. Это должно стоить усилий, чтобы вы смотрели какую ковариацию и контравариантность в общих параметрах как на интерфейсах, так и на делегатах, и дайте этому подходу попытку рефакторинга вашего код.
В настоящее время я считаю, что это должно быть возможным решением:
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");
}
}