Приведите общий класс к интерфейсу - PullRequest
4 голосов
/ 05 сентября 2011

У меня проблема с приведением универсального класса к интерфейсу, который он реализует.

Мой код такой:

interface foo
{
    void foobar();
}

class bar: foo
{
    public void foobar()
    {
        throw new NotImplementedException();
    }
}

теперь у меня есть фабрика, которая создает экземпляры моих классов по интерфейсу, в основном это простое микроядро (локатор служб). Я упросту это здесь. Обычно он ищет класс реализации из конфигов, а фабрика принимает тип как T, но это не имеет значения для моей проблемы.

public static class Factory
{


    public static Lazy<foo> CreateLazyInstance()
    {
        Lazy<foo> instance;


        Type type = typeof(bar);

        Type lazyType = typeof(Lazy<>);
        Type toContruct = lazyType.MakeGenericType(type);

        instance = (Lazy<foo>)Activator.CreateInstance(toContruct);

        return instance;
    }
}

Если не получится при:

instance = (Lazy<foo>)Activator.CreateInstance(toContruct);

и с InvalidCastException утверждают, что невозможно привести тип Lazy<bar> к Lazy<foo>.

Есть ли способ сообщить CLR, что это приведение будет работать или обойти эту проблему?

Ответы [ 3 ]

5 голосов
/ 05 сентября 2011

Нет - Lazy<T> является инвариантом - так что Lazy<string> не является, например, Lazy<object>.(Как указано в комментариях, он не может быть объявлен ковариантным в T, поскольку это класс, а не интерфейс или делегат.)

Однако вы можете легко преобразовать одно в другое:

static Lazy<TOutput> CreateLazyProxy<TInput, TOutput>
    (Lazy<TInput> input) where TInput : TOutput
{
    return new Lazy<TOutput>(() => input.Value);
}

Кроме того, Func<T> является ковариантным, так что это также будет работать:

static Lazy<TOutput> CreateLazy<TInput, TOutput>(Func<TInput> func)
    where TInput : TOutput
{
    return new Lazy<TOutput>(func);
}

(Не то, что вам особенно нужен метод для этого - еслиу вас есть a Func<TInput>, просто создайте Lazy<TOutput> напрямую.)

1 голос
/ 05 сентября 2011

Более простой способ сделать это - передать лямбду в конструктор Lazy. Итак, ваш код будет выглядеть следующим образом:

  public static Lazy<foo> CreateLazyInstance()
  {
     Type type = typeof(bar);
     return new Lazy<foo>(() => (foo)Activator.CreateInstance(type));
  }  
0 голосов
/ 05 сентября 2011

Вы должны сделать общий параметр: new ():

public static Lazy<foo> CreateLazyInstance() where foo : new()

И измените свой код, чтобы найти конструктор и вызвать его:

Type t = typeof(foo);
t.GetConstructor(new type[]{});
return (foo)t.Invoke(new object[]{});
...