Reputation: 20224
Consider I have a TemplateEngine with the three classes:
TemplateEngine
TemplateBase<T> where T : TemplateDataBase
TemplateDataBase
and the sample implementation:
SampleTemplate : TemplateBase<SampleTemplateData>
SampleTemplateData : TemplateDataBase
and now, in the engine, a getter function:
public T GetTemplate<T, U>() where T: TemplateBase<U>, new() where U : TemplateDataBase
{
var template = new T();
...
return template;
}
Since all implementations of TemplateBase
will have exactly one valid value for U
, like the sample, the type of U
can be infered through the choice of T
, and I shouldn't have to provide it to the GetTemplate
method.
Other TemplateData
classes contain completely different data and one shouldn't be able to use the wrong TemplateData
class for a certain template.
If I now remove the U
type parameter from the function call, I get "Incorrect number of type parameters", or, if I remove it from the function definition, the getter is no longer valid because "Cannot resolve U".
If I keep the parameter, I still am not allowed to do so because "There is no implicit reference conversion from SampleTemplate
to TemplateBase<TemplateDataBase>
".
What am I doing wrong here?
Upvotes: 1
Views: 42
Reputation: 4219
Since you are trying to use a type parameter that is a child of the type parameter defined in the GetTemplate
method, you'll need to use a covariant type parameter. Which by definition
Enables you to use a more derived type than originally specified
And since variance modifies can only be applied to interfaces or delegates, you'll need to create one of the two. Here's an example of using a generic interface with a covariant type parameter that allows you the type parameter to be implied:
interface ITemplate<out T> where T : TemplateDataBase
{
Type DataType { get; }
}
class TemplateBase<T> : ITemplate<T> where T : TemplateDataBase
{
public Type DataType => typeof(T);
}
class TemplateDataBase { }
class TemplateEngine
{
public T GetTemplate<T>() where T : ITemplate<TemplateDataBase>, new()
{
var template = new T();
return template;
}
}
class SampleTemplate : TemplateBase<SampleTemplateData> { }
class SampleTemplateData : TemplateDataBase { }
Note the ITemplate<out T>
. This is what actually says that the type parameter is covariant.
And here's an example of the use site where the type is being inferred:
static void Main(string[] args)
{
var engine = new TemplateEngine();
var sampleTemplate = engine.GetTemplate<SampleTemplate>();
Console.WriteLine($"{sampleTemplate.DataType.Name}");
Console.ReadLine();
}
Upvotes: 3