【问题标题】:Polymorphism while iheriting generic class [duplicate]继承泛型类时的多态性[重复]
【发布时间】:2018-01-19 05:34:05
【问题描述】:

假设有一个抽象基类和一个或多个子类:

public abstract class BaseInnerClass
{
    public int Id { get; set; }
}

public class ConcreteInnerClass : BaseInnerClass
{
    public string Name { get; set; }
}

那么,假设有一个通用抽象类具有上述抽象类类型的属性:

public abstract class GeneriAbstractTestClass<T> where T : BaseInnerClass
{
    public T InnerClass { get; set; }    
}

那么让我们创建一个继承自上面类的类:

public class ConcreteTestClass : GeneriAbstractTestClass<ConcreteInnerClass>
{
    public string ConcreteString { get; set; }
}

所以现在一切都准备好问一个问题了;)为什么不可能做到这一点:

 //cannot convert initializer type
 GeneriAbstractTestClass<BaseInnerClass> genericClass = new ConcreteTestClass();

虽然这是允许的:

 //ok
 BaseInnerClass baseInner = new ConcreteInnerClass();

这两个作业有什么区别?

【问题讨论】:

标签: c#


【解决方案1】:

这与抽象类无关。一个更简单的例子是

List<BaseInnerClass> base = new List<ConcreteInnerClass>

A 类型派生自 B 类型这一事实并不意味着 C&lt;A&gt; 类型派生自 C&lt;B&gt; 类型。你的例子有点复杂,但可以用同样的逻辑来解释。

请注意,您可以定义另一种具体类型:

public class EvilConcreteInnerClass : BaseInnerClass
{
} 

如果您想要的是可能的,那么以下方法将起作用:

GeneriAbstractTestClass<BaseInnerClass> genericClass = new ConcreteTestClass();
genericClass.InnerClass = new EvilConcreteInnerClass(); // OK, because the compiler sees `T` as `BaseInnerClass`

genericClass 变量指向T 泛型参数为ConcreteInnerClass 的对象,因此将EvilConcreteInnerClass 分配给该属性会导致运行时异常。

【讨论】:

    【解决方案2】:

    其实。你可以这样做。但是您需要使用协变 out T 泛型指定接口,因为进行这些转换是类型安全的。

    示例

    namespace ConsoleTest
    {
        class Program
        {
            static void Main(string[] args)
            {
                var a = new Generic<Concrete>();
                IGeneric<Base> c = new Generic<Base>();
                c = a;
            }
        }
    
        public interface IGeneric<out T> where T: Base
        {
            T Inner { get; }
        }
    
        public class Generic<T> : IGeneric<T>
            where T : Base
        {
            public T Inner { get; set; }
        }
    
        public class Concrete : Base
        {
        }
    
        public class Base
        {
        }
    }
    

    如果指定协变通用模板,代表也不受限制。

    这意味着只要您使用只读通用属性,您想要的那些转换就可以了。因此,就像@Kapol 所说的那样,并为您提供了示例,为什么允许设置属性或将 T 传递给函数不是类型安全的。

    总结

    如果您想使用这些类型的强制转换,请使用 ReadOnly 接口。

    【讨论】:

    • 类不能是协变的,这就是问题所在。
    • 我同意,但这只是 C# 的设计。而已。例如,在 C++ 中,您可以使用 const 创建协变类。
    • 但这不是 C++。
    • 我认为提及 C++ 语法对于更广泛的图片会很有用。
    猜你喜欢
    • 2014-06-01
    • 2011-08-19
    • 2011-08-17
    • 2022-10-07
    • 1970-01-01
    • 2020-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多