考虑我有一个带有三个类的TemplateEngine:TemplateEngine
TemplateBase<T> where T : TemplateDataBase
TemplateDataBase
和示例实现:SampleTemplate : TemplateBase<SampleTemplateData>
SampleTemplateData : TemplateDataBase
现在,在引擎中,有一个吸气功能:
public T GetTemplate<T, U>() where T: TemplateBase<U>, new() where U : TemplateDataBase
{
var template = new T();
...
return template;
}
由于
TemplateBase
的所有实现都将为U
确切地提供一个有效值,就像示例一样,可以通过选择U
来推断T
的类型,而我不必将其提供给GetTemplate
方法。其他
TemplateData
类包含完全不同的数据,并且某个模板不能使用错误的TemplateData
类。如果现在从函数调用中删除
U
类型参数,则会得到“类型参数数目不正确”,或者,如果从函数定义中将其删除,则由于“无法解析U”,吸气剂将不再有效。如果保留该参数,则仍然不允许这样做,因为“没有从
SampleTemplate
到TemplateBase<TemplateDataBase>
的隐式引用转换”。我在这里做错了什么?
最佳答案
由于您要尝试使用类型参数,而该类型参数是GetTemplate
方法中定义的类型参数的子级,因此需要使用协变类型参数。根据定义
使您可以使用比最初指定的类型更多的派生类型
由于方差修改只能应用于接口或委托,因此您需要创建两者之一。这是一个使用generic interface with a covariant type parameter的示例,它允许您隐含类型参数:
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 { }
注意
ITemplate<out T>
。这实际上是说类型参数是协变的。这是一个推断类型的使用站点的示例:
static void Main(string[] args)
{
var engine = new TemplateEngine();
var sampleTemplate = engine.GetTemplate<SampleTemplate>();
Console.WriteLine($"{sampleTemplate.DataType.Name}");
Console.ReadLine();
}