为什么要限制方法和属性继承的返回类型,使得后代不被允许?

4

假设有以下情况:

class ClassParent { }
interface IClassProvider { ClassParent Get(); }

为什么这个IClassProvider实现是非法的:
class ClassChild : ClassParent, IClassProvider
{
    ClassChild Get() { return this; }
}

它还不能用于继承属性和实现基类而不是接口的情况,就像示例中一样。

ClassChild ClassParent。为什么它不能编译?编译器显然知道类的编译时类型,因此以下内容有效:

void DoSomething(object o) { ... };
void DoSomething(ConcreteClass c) { ... };

DoSomething(new ConcreteClass()); //Calls the second overload of the method because of static type resolving

在分层场景下,这迫使我拥有大量的代理方法,在底层情况清晰的情况下,我的代码变得杂乱无章。坦白地说,如果支持这一点,我想不出任何问题或歧义。对于像第二个示例中的重载那样的编译时(静态)解析,我会感到高兴。

编辑:我知道编译器期望以下内容:

class ClassChild : ClassParent, IClassProvider
{
    ClassParent Get() { return this; }
}

我知道这会起作用,但我想要一个解释为什么它不被支持或在某种情况下会引起问题或模糊不清,这比重载更糟。而重载是被支持的。
编辑2: 这似乎又是一个旧问题的副本,MS本人在这个SO问题中回答了这个问题。我将@Euphoric的帖子标记为答案,因为他提供了一个功能名称,有助于找到“解决方案”。
5个回答

4
我认为你要找的功能是名为协变返回类型的功能。
至于“为什么”它没有被实现,没有人知道。在我看来,这个功能并不是那么必要,因此它就没有被实现。
而且特别是在你的情况下,可以很容易地使用显式接口实现来解决。

感谢您提供功能的名称。我知道显式接口实现,但我的情况是一个更大的场景,只是为了适应问题而简化了。 - Boris B.
我同意这个观点... 我看不出为什么它不能工作,但是通过显式接口实现,它真的不需要,因为你可以在类上专门为接口设置一个Get函数,并且如果你想要该方法在使用ClassChild类型时返回ClassChild,则可以为类设置一个特定的函数。 - BlueMonkMN

2

虽然不完全是你的问题,但你可以实现类似于这样的东西:

class ClassParent { }

    interface IClassProvider<T> where T: ClassParent
    {
        T Get();
    }

    class ClassChild : ClassParent,IClassProvider<ClassChild>
    {

        public ClassChild Get()
        {
            return this;
        }
    }

或者,对于更极端的情况(这可以提供更多的类型安全性)。
public class ClassParent { }

    interface IClassProvider<T> where T : ClassParent, IClassProvider<T>
    {
        T Get();
    }

    class ClassChild : ClassParent, IClassProvider<ClassChild>
    {

        public ClassChild Get()
        {
           return this;
        }
    }

编辑:

第二个版本在类型安全方面更加安全,因为以下代码不会通过编译:

public class SomeOtherChildClass : ClassParent { }

class ClassChild : ClassParent, IClassProvider<SomeOtherChildClass>
    {

        public SomeOtherChildClass Get()
        {
           return this;
        }
    }

你可以写出以下类似的内容,虽然不完美:

但是你可以这样写,所以并不完美:

public class SomeOtherChildClass : ClassParent, IClassProvider<SomeOtherChildClass> { //implementation }

class ClassChild : ClassParent, IClassProvider<SomeOtherChildClass>
    {

        public SomeOtherChildClass Get()
        {
           return something;
        }
    }

我不确定类型安全是否是完全正确的术语。 我的目的是创建一个只有实现了提供程序的对象才能提供的对象。


不错,模板化返回类型并对其进行限制,+1。这将比显式实现解决方案(需要两种方法来实现)减少代码行数。我很好奇,为什么第二个选项比第一个更“类型安全”? - Boris B.

2

IClassProviderGet() 方法期望它的实现者返回任何继承自或是实例化于 ClassParent 的东西。

你违反了接口规定的契约,让其实现者只返回 ClassParent 的一个特定子类对象,由于类的单一继承性质,所以这是不被允许的。


我不认为这会成为一个问题,你能给个场景吗?只要每个ClassChild都是ClassParent(而且确实如此),就不会违反任何正式契约。我可以按照编译器的喜好来实现它,并编写ClassParent Get() { return this; },这样我总是会返回一个特定类型的实例。 - Boris B.

1

现在有意义了。

签名必须完全匹配,就像这样

class ClassChild : ClassParent, IClassProvider
{
    ClassParent Get() { return this; }
}

当然我知道它应该是什么样子的,我的问题是为什么它不被支持,即它可能存在哪些潜在问题。 - Boris B.

1
什么是使用Euphoric建议的显式接口实现存在的问题?
class ClassChild : ClassParent, IClassProvider
{

   public ClassParent IClassProvider.Get()
   {
      return Get();
   }

   public ClassChild Get()
   {
      return this;
   }
}

我没有想到可以使用显式实现来达到那个目的。谢谢,这帮了很大的忙。虽然问题仍然存在,为什么微软认为协变返回类型不是一个好主意。我发布的线程引用了Anders的观点,他认为这不是一个好主意,但没有解释原因。 - Boris B.

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