Reputation: 42229
Say for example I have this base class
class Mangler<TInput, TOutput>
{
}
And then I make a couple of derived classes
class StringToBytesMangler : Mangler<string, byte[]>
{
}
class IntToGuidMangler : Mangler<int, Guid>
{
}
How do I store a collection of Mangler<TInput, TOutput>
where TInput
and TOutput
may be different at any given time?
i.e.
List<Mangler<?, ?>> list = new List<Mangler<?, ?>>();
list.Add(new StringToBytesMangler());
list.Add(new IntToGuidMangler());
Is this possible?
Upvotes: 2
Views: 2110
Reputation: 27009
The whole idea behind generics is to have generic code so type of that class can be treated the same. From what you have posted, it is not easy to see what generic code you have.
Below I have a class that has some generic code:
class Mangler<TInput, TOutput>
where TInput: ITInput
where TOutput: ITOutput {
public TInput Input { get; set; }
public TOutput Output { get; set; }
public bool IsInputAGuid() {
if (Guid.Parse(this.Input.SomeThing) == this.Output.SomeGuid ) {
return true;
}
return false;
}
}
You can see in the above class, when it parses a string to a Guid
from this.Input.Something
and then it performs ==
on it with this.Ouput.SomeGuid
, the compiler is happy because we have made the constraint that TInput
must implement the interface ITInput
so the compiler knows this line will work and Input
will have Something
as a string
property:
Guid.Parse(this.Input.SomeThing)
The compiler does not care what the concrete type is so long as Something
is available. It is the same idea for TOuput
but the compiler expects that it implements ITOutput
so it expects a Guid
in SomeGuid
. This is why the compiler is happy to parse a string to a guid and then perform the ==
operator on it with another thing which is also a Guid
.
Here are the interfaces and some classes which implement them:
internal interface ITInput {
string SomeThing { get; set; }
}
internal interface ITOutput {
Guid SomeGuid { get; set; }
}
internal class AnotherInput : ITInput {
public string SomeThing { get; set; }
}
internal class SomeInput : ITInput {
public string SomeThing { get; set; }
}
internal class SomeOutput : ITOutput {
public Guid SomeGuid { get; set; }
}
internal class SomeOtherOutput : ITOutput {
public Guid SomeGuid { get; set; }
}
Finally, here is the usage where we can treat these generically:
var manglers = new List<Mangler<ITInput, ITOutput>>();
manglers.Add( new Mangler<ITInput, ITOutput>
{ Input = new SomeInput(), Output = new SomeOutput() } );
manglers.Add( new Mangler<ITInput, ITOutput>
{ Input = new AnotherInput(), Output = new SomeOutput() } );
foreach( var thisMangler in manglers ) {
var input = thisMangler.Input;
var output = thisMangler.Output;
var success = thisMangler.IsInputAGuid();
}
You can see in the foreach
regardless of the concrete type, we can call Input
, Output
and IsInputAGuid()
on all of them.
So in your code find what code is generic and then apply the above technique to it. You can either use interfaces or a base class for your constraints.
Upvotes: 0
Reputation: 17605
If I understood the question correctly, this is not possible the way you tried it. The types StringToBytesMangler
and IntToGuidMangler
do not derive from the same type. You could introduce a shared base type, but I recommend reconsidering the design - even if they could be stored in the same collection, they would syntactically have nothing in common (at least it isn't shown in the question).
Upvotes: 2
Reputation: 292405
You need a non-generic Mangler
base class.
List<Mangler> list = new List<Mangler>();
list.Add(new StringToBytesMangler());
list.Add(new IntToGuidMangler());
Of course, this means you also need to have non-generic versions of the methods that depend on TInput
or TOutput
.
Upvotes: 3