Подтвердить что ты не робот

Экзистенциальные типы в С#?

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

В принципе, я хочу иметь такой код:

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

Ответ 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");
    }
}