首页 > 解决方案 > 具有非多态子类和抽象的多态基类

问题描述

考虑我有一个包含三个类的 TemplateEngine:

和示例实现:

现在,在引擎中,一个 getter 函数:

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从函数调用中删除类型参数,我会得到“类型参数的数量不正确”,或者,如果我从函数定义中删除它,getter 将不再有效,因为“无法解析 U”。

如果我保留参数,我仍然不允许这样做,因为“没有从SampleTemplateto的隐式引用转换TemplateBase<TemplateDataBase>”。

我在这里做错了什么?

标签: c#polymorphism

解决方案


由于您尝试使用作为GetTemplate方法中定义的类型参数的子类型参数的类型参数,因此您需要使用协变类型参数。根据定义,哪个

使您能够使用比最初指定更多的派生类型

而且由于方差修改只能应用于接口或委托,因此您需要创建两者之一。这是一个使用带有协变类型参数的泛型接口的示例,它允许您隐含类型参数:

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();
}

在此处输入图像描述


推荐阅读