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?
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.)
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));
}
You must do your foo generic parameter : new():
public static Lazy<foo> CreateLazyInstance() where foo : new()
And change your code to find a constructor and call it:
Type t = typeof(foo);
t.GetConstructor(new type[]{});
return (foo)t.Invoke(new object[]{});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With