考虑我有一个带有三个类的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”,吸气剂将不再有效。

如果保留该参数,则仍然不允许这样做,因为“没有从SampleTemplateTemplateBase<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();
}


c# - 具有非多态子类和抽象的多态基类-LMLPHP

09-25 20:35