Cast generic class to interface

2019-04-10 16:15发布

问题:

I have a problem with casting a generic class to the interface it is implementing.

My code is like this:

interface foo
{
    void foobar();
}

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

now I have my factory that creates instances of my classes by the interface, mostly a simple microkernel (service locator). I will simplify it here. Normally it will look up the implementing class from the configs and the factory take the type as T but that doesn't matter for the problem I have.

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

If will fail at:

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

and claim with an InvalidCastException that it is not possible to cast the type Lazy<bar> to Lazy<foo>.

Is there any way to tell the CLR that this cast will work or to workaround this problem?

回答1:

No - Lazy<T> is invariant - so a Lazy<string> is not a Lazy<object> for example. (As pointed out in comments, it couldn't be declared as covariant in T, as it's a class, not an interface or delegate.)

However, you can convert one to the other easily enough:

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

Also, Func<T> is covariant, so this will work too:

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

(Not that you particularly need a method for that - if you've got a Func<TInput>, just construct a Lazy<TOutput> directly.)



回答2:

An easier way to do this would be to pass in a lambda to the Lazy constructor. So, your code would look like the following:

  public static Lazy<foo> CreateLazyInstance()
  {
     Type type = typeof(bar);
     return new Lazy<foo>(() => (foo)Activator.CreateInstance(type));
  }  


回答3:

You must do you foo generic parameter : new():

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

And change you code to find a constructor and call it:

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