I have a class Company
that holds a list of different IFactory<IPart>
; e.g. an EngineFactory
.
public class Company
{
private Dictionary<Type, IFactory<IPart>> _factories;
public Company()
{
_factories = new Dictionary<Type, IFactory<IPart>>();
_factories[typeof (Engine)] = new EngineFactory();
}
public void SendOrderIntakeToFactory(IPart part)
{
}
}
The EngineFactory
looks like this:
public class EngineFactory : IFactory<Engine>
{
public void Produce(Engine part)
{
}
}
And the IFactory
interface:
public interface IFactory<T> where T : IPart
{
void Produce(T part);
}
This will result in a compiler error:
Cannot implicitly convert type 'EngineFactory' to 'IFactory'. An explicit conversion exists (are you missing a cast?)
on this line: _factories[typeof (Engine)] = new EngineFactory();
Ok that makes sense to me, without specifying the variance this will never work. So I tried to add the out
keyword to the generic type T
, but that will force me to remove the T
as method parameter (because it's not allowed to use <out T>
as input parameter):
public interface IFactory<out T> where T : IPart
{
void Produce(IPart part);
}
This clearly breaks my generic design. I am able to produce Wheel
s at my EngineFactory
.
I understand the challenge lies in these two requirements:
IFactory<IPart>
void Produce(T part);
implementation in the factories.Is there any way of achieving this?
You can mark generic type parameter as covariant if it used only as return type of methods. Change Produce
method to return part and all will work
public interface IFactory<out T> where T : IPart
{
T Produce();
}
You can't use covariant type parameter if it is used as method parameter.
BTW it's really strange factory which accepts objects instead of creating them.
UPDATE you can use runtime type definition with dynamic:
public class Company
{
private Dictionary<Type, dynamic> _factories;
public Company()
{
_factories = new Dictionary<Type, dynamic>();
_factories[typeof(Engine)] = new EngineFactory();
}
public void SendOrderIntakeToFactory(IPart part)
{
_factories[part.GetType()].Produce((dynamic)part);
}
}
When you will call
company.SendOrderIntakeToFactory(new Engine());
Then EngineFactory
will be selected and it's Produce
method will be called with parameter of runtime type Engine
.