3

今天我们用一种close()方法发生了一件奇怪的事情。

这是有疑问的代码:

interface ICloseable
{
    void Close();
}

public class Closer
{
    public void Close()
    {
        Console.WriteLine("closed");
    }
}

public class ConcreteCloser : Closer, ICloseable 
{
}


class Program
{
    static void Main(string[] args)
    {
        var concrete = new ConcreteCloser();
        concrete.Close();
        Console.ReadKey();
    }
}

所以问题是:

基类不实现接口。

为什么编译器接受该Closer.close()方法作为接口方法的实现?

为什么 Visual C# 2010 Professional 和 Eclipse 中都没有警告?

对我来说,C# 和 Java 在这种情况下有点鸭式。

有人可以解释一下这背后的语言考虑吗?由于 C# 和 Java 都以相同的方式执行此操作,因此似乎有充分的理由。

4

2 回答 2

2

接口是一个契约。该合同声明该类型具有给定签名的方法。不需要在类型本身中定义或重新定义相关成员。

这与鸭子类型无关。鸭子类型意味着成员在编译时没有被解析;它们在运行时解决。在这里,编译器能够确定该类在编译时实现了该方法。

于 2013-06-17T18:10:49.893 回答
2

对于标题中的问题

接口不是 Duck Typing。

主格打字

.. 数据类型的兼容性和等效性由显式声明和/或类型的名称决定

这包括 Java/C# 类和接口。所有类型和类型之间的关系都由 name定义。

结构类型

.. 兼容性和等价性由类型的实际结构或定义决定,而不是由其名称或声明位置等其他特征决定。

这包括Scala 结构类型和 C++ 模板。

鸭子打字

.. 对象的方法和属性决定了有效的语义

这包括动态类型语言(例如 Ruby、Python、JavaScript)和 C# 的dynamic. 我还暂时断言 Duck Typing 是 Structural Typing 的子集/无类型形式;它与主格类型正交。

对于其他问题

为什么编译器接受 Closer.close() 方法作为接口方法的实现?

因为 Close 方法是公共的并且具有一致的签名。由于 ConcreteCloser 从 Closer 继承,它还获得所有基类方法——根据继承子类型和Liskov 替换原则(并非所有 OOP 语言都使用 LSP)——因此符合 ICloseable然后它选择按名称实现 ICloseable 接口。我不确定这里会出现什么警告。

如果 C# 是 Structurally(或 Duck)类型的,那么 Closer 可以用来代替 ICloseable,但它不能; ICloseable c = new Closer()无效,因为 Closer 未定义为主格相关的ICloseable

对我来说,在这种情况下,C# 和 Java 有点像鸭子类型。

不; 除非dynamic在 C# 中讨论。往上看。

有人可以解释一下这背后的语言考虑吗?由于 C# 和 Java 都以相同的方式执行此操作,因此似乎有充分的理由。

这是由语言设计选择的接口是在单一继承模型中支持主名类型的一种方法。Scala(和 Ruby)支持 Traits;C++ 支持多重继承。Eiffel 支持 MI 并在类型级别上打破 LSP。去想 - 没有“一个正确的方法”。

于 2013-06-17T18:33:05.357 回答