如何在继承的函数中重新定义在主体接口中定义的函数而不覆盖?

问题描述 投票:0回答:3

有人可以解释同一类上的相同函数的行为有何不同吗?

using System;

public class HelloWorld
{
    public static void Main(string[] args)
    {
        MyUpperScript mAsdfScript = new MyUpperScript();
        IInterface mAsdfInterface = mAsdfScript;
        mAsdfScript.Foo();
        mAsdfInterface.Foo();
    }
}

public class MyUpperScript : MyScript
{
    public void Foo()
    {
        Console.WriteLine("Upper Script");
    }
}
public class MyScript : IInterface
{

}

public interface IInterface
{
    public void Foo()
    {
        Console.WriteLine("Interface");
    }
}

我期待它在 Foo() 调用上都写“Upper Script”

请注意,如果我也从接口派生第二个类,它会按照我期望的方式工作 - 都打印“Upper Script”:

// now deriving for the interface too:
public class MyUpperScript : MyScript, IInterface
{
    public void Foo()
    {
        Console.WriteLine("Upper Script");
    }
}
c# inheritance interface overriding default-interface-member
3个回答
5
投票

我怀疑这是因为

类不会从其接口继承成员

(来自 here),意味着

MyScript
实际上没有方法
Foo
可供
MyUpperScript
覆盖。当(现在我真的在这里猜测......!)您将变量重新转换为接口类型时,虚拟方法查找不再知道在
Foo
中查找
MyUpperScript
,因此会退回到默认实现。

不过,这对我来说仍然很奇怪,它并没有推断出

MyUpperScript
间接实现了
IInterface
- 但这可能只是我关于它如何工作的心理模型一直是错误的,而且没有机会这样做直到您拥有默认的接口成员为止,以产生任何实际的变化。 (在这些之前,隐式实现的接口的所有成员也将从显式实现的超类继承。)就我个人而言,我认为如果修复了这个问题,那么该语言可能会更有意义,以便这两个原因的行为就像您指定了接口一样明确地(如在你的尾注中),但我无法找到任何文本来向我表明这是否是一个有意识的决定,以及如果它确实是有意识的,其理由是什么。

如果您尝试直接在

MyScript
上使用它(如下所示),您应该会收到编译器错误:

MyScript myScript = new MyScript();
myScript.Foo(); // compiler error here

3
投票

我认为您的代码在功能上等同于以下内容:

public class MyUpperScript : MyScript
{
    public void Foo()
    {
        Console.WriteLine("Upper Script");
    }
}
public class MyScript : IInterface
{
    void IInterface.Foo()
    {
        Console.WriteLine("Interface");
    }
}

public interface IInterface
{
    void Foo();
}

请注意,

MyScript
显式实现了
IInterface.Foo()
。因此
Foo
上没有
MyScript
实现,因此
MyUpperScript
可以自由地实现自己的
Foo()


2
投票

此行为是设计使然,这样默认接口成员的行为与抽象接口成员一致。考虑您的示例的这种变体:


public class HelloWorld
{
    public static void Main(string[] args)
    {
        MyUpperScript mAsdfScript = new MyUpperScript();
        IInterface mAsdfInterface = mAsdfScript;
        mAsdfScript.Foo();
        mAsdfInterface.Foo();
    }
}

public class MyUpperScript : MyScript
{
    public new void Foo() => Console.WriteLine("Upper Script");
}
public class MyScript : IInterface
{
    public void Foo() => Console.WriteLine("MyScript");
}

public interface IInterface
{
    public void Foo();
}

输出是相同的:

Upper script
MyScript

首先,请注意

new
上需要
MyUpperScript.Foo()
修饰符。即使
MyScript
显式声明它实现
IInterface
,也需要
new
修饰符。这是因为接口成员默认情况下不是
virtual
MyScript.Foo()
不需要
override
关键字,因为它不会重写任何方法;它正在实现一个接口。

添加

IInterface.Foo()
的默认实现不会更改任何这些规则。如果您添加
IInterface.Foo()
的默认实现,但仍保留
MyScript
中的实现,编译器会警告您应该在
new
上添加
MyUpperScript.Foo()
修饰符。如果没有实现
MyScript.Foo()
MyUpperScript.Foo()
将不再发出警告。它实现了接口成员。

这种行为确保了今天的设计目标得到满足:

  1. 向已发布的接口添加默认接口成员不会破坏任何现有代码。即使实现修改后的接口的类(即使是间接实现)已经包含具有相同签名的方法,也是如此。
  2. 实现或重新实现默认接口成员的行为尽可能接近于实现或重新实现没有默认实现的接口成员。
© www.soinside.com 2019 - 2024. All rights reserved.