C#协变性和继承

7

我很好奇为什么在抽象基类中实现的接口不能满足子类中的要求。以下是一个例子:

public interface IBase { }
public interface IConcrete : IBase { }

public interface IBaseManager<out T>
    where T : IBase
{
    T Create();
    IEnumerable<T> SelectAll();
}

public interface IConcreteManager : IBaseManager<IConcrete> { }

public abstract class Base : IBase { }

public class Concrete1 : Base, IConcrete { }

public abstract class BaseManager<T> : IBaseManager<T> where T : class, IBase
{
    #region IBaseManager<T> Members

    public T Create()
    {
        throw new NotImplementedException();
    }

    public IEnumerable<T> SelectAll()
    {
        throw new NotImplementedException();
    }

    #endregion
}

public class ConcreteManager : BaseManager<Concrete>, IConcereteManager
{
             //error occurs here
} 

这是生成的错误信息:
'ConsoleApplication4.ConcreteManager'没有实现接口成员'ConsoleApplication4.IBaseManager<ConsoleApplication4.IConcrete>.Create()'。 'ConsoleApplication4.BaseManager<ConsoleApplication4.Concrete>.Create()'无法实现'ConsoleApplication4.IBaseManager<ConsoleApplication4.IConcrete>.Create()',因为它没有匹配的返回类型'ConsoleApplication4.IConcrete'。
如果我将这些方法添加到ConcreteManager类中,一切都会很好,并且编译器也很高兴。
public new IConcrete Create()
{
    return base.Create();
}

public new IEnumerable<IConcrete> SelectAll()
{
    return base.SelectAll();
}

如果仅仅返回基类方法的结果就足够了,为什么还要添加这些方法呢?为什么编译器不能调用基类中的方法呢?

我认为扩展接口是问题所在,尝试移除它。 - James Black
你不想要这个吗?public class ConcreteManager : BaseManager<IConcrete>, IConcereteManager - Bala R
3个回答

7
正如John所正确指出的,C#语言不支持返回类型协变。就算语言支持,CLR也不支持,所以即使我们想要实现这个特性,唯一的方法是默默地生成你必须自己添加的代码。
避免编写那些存根方法给开发者带来的微小好处并不能证明实现更通用的协变特性所需付出的巨大代价,因此我们从未这样做过。

那么说协方差实际上是对返回值的赋值,这种说法是否准确? - Seattle Leonard
@Seattle Leonard:我不理解这个问题。协变性是指一个映射保留一个关系方向的属性。在返回类型协变的情况下,我们有两个关系:赋值兼容性和虚拟槽兼容性。在类型到返回这些类型方法的映射“协变”相对于这些关系的语言中,如果X与Y具有赋值兼容性,则返回X的方法与返回Y的方法具有虚拟槽兼容性。 - Eric Lippert
@EricLippert,我不会说协变返回类型只是为开发人员提供的小好处。在我的最新项目中,我添加了近百个这样的“不变修复”。或者我总体上做错了什么吗? - KnorxThieus
为了更好地概述,我总是需要使用OtherClass(从IOtherInterface派生)方法/属性来实现IMyInterface接口的方法/属性。这样,MyClass就可以实现IMyInterface接口并具有所需的修复功能。 - KnorxThieus

5

看起来你假定了返回类型协变性,因为ConcreteManager(作为IConcreteManager)期望Create()SelectAll()方法的返回类型分别为IConcreteIEnumerable<IConcrete>,而基类没有提供。

你会遇到这些错误是因为C#不支持返回类型协变性。


1

当你实现一个接口/抽象类时,必须使用相同的签名。在这里查看

不要让泛型使你困惑,这与没有泛型时没有区别。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接