I'm having a hard time with a problem in C#.
In the project I'm working on, there are some consumable
classes which store information and there are consumer
classes which uses those consumable classes.
I've replicated the thing in a simpler way like this:
using System;
using System.Collections.Generic;
interface IBase
{
int x {get;}
}
class BaseClass : IBase
{
public int x {get; set;}
}
interface IDerived : IBase
{
int y {get;}
}
class DerivedClass : BaseClass, IDerived
{
public int y {get; set;}
}
interface IConsumer<in T> where T: class, IBase
{
void Feed(T arg);
}
class BaseConsumer : IConsumer<IBase>
{
public void Feed(IBase arg)
{
Console.WriteLine(arg.x);
}
}
class DerivedConsumer : IConsumer<IDerived>
{
public void Feed(IDerived arg)
{
Console.WriteLine(arg.y);
}
}
public class Program
{
public static void Main()
{
List<IConsumer<IDerived>> consumers = new List<IConsumer<IDerived>>();
consumers.Add(new BaseConsumer());
consumers.Add(new DerivedConsumer());
DerivedClass d = new DerivedClass() { x = 3, y = 5};
foreach (IConsumer<IDerived> consumer in consumers)
consumer.Feed(d);
}
}
This works completely fine. However, I want to have some middle
consumers too, which takes another consumer
object in its constructor (or another method), which redirects the Feed call to the objects. For example:
class MiddleConsumer<T> : IConsumer<T> where T : class, IBase
{
private IConsumer<T> _consumer;
public MiddleConsumer(IConsumer<T> consumer)
{
_consumer = consumer;
}
public void Feed(T arg)
{
_consumer.Feed(arg);
}
}
And I'd use it like this:
consumers.Add(new MiddleConsumer<IBase>(new BaseConsumer());
consumers.Add(new MiddleConsumer<IDerived>(new DerivedConsumer());
But I'll be using MiddleConsumer
with other concrete Consumer
classes, such as BaseConsumer
and DerivedConsumer
. Therefore I shouldn't have to specify the T
parameter explicitly, it can just get it from the actual Consumer
s type. Let me clarify. I want to have something like this: (note this isn't valid C# code)
class MiddleConsumer<IConsumer<T>> : IConsumer<T>
{
private IConsumer<T> _consumer;
public MiddleConsumer(IConsumer<T> consumer)
{
_consumer = consumer;
}
public void Feed(IDerived arg)
{
_consumer.Feed(arg);
}
}
However, -as expected- this doesn't work due to having that weird generic parameter.
I want to be able to create a MiddleConsumer object with a Consumer which I don't know its base type. I mean, I want to be able to do this:
consumers.Add(new MiddleConsumer<BaseConsumer>(new BaseConsumer());
consumers.Add(new MiddleConsumer<DerivedConsumer>(new DerivedConsumer());
I don't want to check the base type of -for example- BaseConsumer
every time I want to use it with MiddleConsumer
, since the compiler can just look at it when I use a Consumer
with MiddleConsumer
. Notice that there could be dozens of different Consumer
classes.
My question is: is there a way to make C# compiler infer the consumable type for MiddleConsumer
from its generic parameter?
Thank you for your help!
C# doesn't support type inference on constructors, but what you can do is delegate calls to the constructors through a static method:
public static class MiddleConsumer
{
public static MiddleConsumer<T> Create<T>( IConsumer<T> consumer ) where T : class, IBase
{
return new MiddleConsumer<T>( consumer );
}
}
Now you shouldn't need to specify a generic argument at all:
consumers.Add(MiddleConsumer.Create(new BaseConsumer());