Преобразование универсального типа - PullRequest
0 голосов
/ 19 мая 2018

У меня есть два универсальных класса, реализующих один интерфейс.

public interface Interface1
{
    //implementation
}
public interface Interface2<T>
{
    //implementation
}
class Class1<T>: Interface2<T> where T : Interface1
{
    //implementation
}
class Class2<T>: Interface2<T>
{
    //implementation
}

Я хотел бы написать метод, который возвращает объект одного из этих классов, в зависимости от типа T.

 Interface2<T> GetObject<T>()
{
    if (typeof(Interface1).IsAssignableFrom(typeof(T)))
    {
        //error
        return new Class1<T>();
    }
    return new Class2<T>();
}

Реализация Class1 должна быть ограничена типами, реализующими интерфейс.Есть ли способ преобразовать тип T в Interface1?Теперь я получаю сообщение об ошибке: тип «T» нельзя использовать в качестве параметра типа «T» в универсальном типе или методе «Class1».Конвертирование или преобразование параметров типа из «T» в «Test.Interface1» не выполняется.

1 Ответ

0 голосов
/ 19 мая 2018

Полное отражение будет:

return (Interface2<T>)Activator.CreateInstance(typeof(Class1<>).MakeGenericType(typeof(T)));

, но оно медленное (медленнее по сравнению с new Foo()) ... Я не нахожу другого пути.Обратите внимание, что вы уже частично двигаетесь в направлении отражения (IsAssignableFrom)

Мммм, используя "кэширование" статических классов, мы можем немного обмануть ... Мы можем произвести во время выполнения точный необходимый коддля создания new Class1<T> и его кеширования.

Первая версия

static class Maker<T>
{
    public static Func<Interface2<T>> Func { get; private set; }

    public static Interface2<T> New()
    {
        if (Func == null)
        {
            Func = Expression.Lambda<Func<Interface2<T>>>(Expression.New(typeof(Class1<>).MakeGenericType(typeof(T)))).Compile();
        }

        return Func();
    }
}

Я использую дерево выражений, которое выполняет new Class1<T>.Тогда:

static Interface2<T> GetObject<T>()
{
    if (typeof(Interface1).IsAssignableFrom(typeof(T)))
    {
        return Maker<T>.New();
    }
    return new Class2<T>();
}

Но все же мы можем сделать что-то большее.Учитывая тип T, результат if в GetObject() может быть предварительно рассчитан и кэширован.Мы перемещаем целое GetObject() внутри дерева выражений.

static class Maker2<T>
{
    public static Func<Interface2<T>> Func { get; private set; }

    public static Interface2<T> New()
    {
        if (Func == null)
        {
            if (typeof(Interface1).IsAssignableFrom(typeof(T)))
            {
                Func = Expression.Lambda<Func<Interface2<T>>>(Expression.New(typeof(Class1<>).MakeGenericType(typeof(T)))).Compile();
            }
            else
            {
                Func = Expression.Lambda<Func<Interface2<T>>>(Expression.New(typeof(Class2<>).MakeGenericType(typeof(T)))).Compile();
            }
        }

        return Func();
    }
}

, а затем

static Interface2<T> GetObject2<T>()
{
    return Maker2<T>.New();
}

Решение, использующее дерево выражений, очень медленное при первом использовании для каждоговведите T, потому что он должен создать дерево выражений и скомпилировать его, но тогда это становится очень быстрым.Это по сравнению с версией, которая использует Activator.CreateInstance, которая каждый раз работает медленно: -)

...