alhazen alhazen - 3 days ago 5
C# Question

C# - creating a class whose constructor takes generic param results in casting exception

I have

IInfo
and its generic version:

public interface IInfo
{
IInput Input { get; }
}

public interface IInfo<T> : IInfo where T : IInput
{
new T Input { get; }
}


Class implementation:

public class Info : IInfo<IInput>
{
public IInput Input { get; }

public Info (IInput input) {}
}


Factory to create IOutput from IInput:

public class GenericFactory<TInput, TOutput> where TInput : IInput where TOutput : IOutput
{
public IOutput Create(IInfo info)
{
ConstructorInfo cInfo = typeof(TOutput).GetConstructor(new[] { typeof(IInfo<TInput>) });
object output = cInfo.Invoke(new object[] {cInfo});
}
}


To test the above code:

public class TestInput : IInput
{
}

public abstract class AbstractOutput<TInput> : IOutput where TInput : IInput
{
}

public class TestOutput: AbstractOutput<TestInput>
{
public TestOutput(IInfo<TestInput> info)
{

}
}

public void Test()
{
IInput input = new TestInput();
IInfo info = new Info(input);

var factory = new GenericFactory<TestInput, TestOutput>();
IOutput output = factory.Create(info);
}


I get the following error:

Object of type 'Info' cannot be converted to type'Info<TestInput>'.


Side note: I'm open to any suggestions to simplify/re-write the code in a different way.

Rob Rob
Answer
public TestOutput(IInfo<TestInput> info)
{

}

Is expecting an IInfo<TestInput> explicitly. However, you're trying to call it with IInfo<IInput> (which is what Info is designed to be).

To make it clear, you could also write:

IInput input = new OtherInput();
IInfo info = new Info(input);

var factory = new GenericFactory<TestInput, TestOutput>();
IOutput output = factory.Create(info);

And now you've provided IInfo<OtherInput> to something expecting IInfo<TestInput>

You would need to make IInfo<T> contravariant to allow it to be cast, for example:

public interface IInfo<in T> : IInfo 
    where T : IInput
{
    //new T Input { get; }
}

But note that it's illegal to return T when with a contravariant interface. The alternative is to make Info generic, and change Create to accept IInfo<TInput>. That latter gives you the benefit of a compile-time error when trying to pass IInfo<OtherInput> to Create(), rather than a run-time error

Comments