考虑一下,我有一个包含三个类的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>
的隐式引用转换”。
我在这里做什么错了?
答案 0 :(得分:3)
由于您要尝试使用类型参数,而该类型参数是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();
}