今天我们用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都以同样的方式执行,因此似乎有充分的理由。
答案 0 :(得分:2)
界面是合约。该合同规定该类型具有给定签名的方法。不需要在类型本身中定义或重新定义相关成员。
这与鸭子打字无关。鸭子打字意味着成员在编译时没有得到解决;它们在运行时被解决了。在这里,编译器能够确定该类在编译时实现方法。
答案 1 :(得分:2)
接口不是Duck Typing。
..数据类型的兼容性和等价性由显式声明和/或类型名称决定。
这包括Java / C#类和接口。类型之间的所有类型和关系都按名称定义。
..兼容性和等同性由类型的实际结构或定义决定,而不是由其名称或声明地点等其他特征决定。
这包括Scala Structural Types和C ++模板。
..对象的方法和属性决定了有效的语义。
这包括动态类型语言(例如Ruby,Python,JavaScript)和C#的dynamic
。我还试探性地断言Duck Typing是结构类型的子集/无类型形式;它与Nominative Typing正交。
为什么编译器接受Closer.close()方法作为接口方法的实现?
因为Close方法是公共的并且具有符合标记的签名。 由于ConcreteCloser继承自Closer,它还获取所有基类方法 - 每Inheritance Subtyping和Liskov Substitution Principle(并非所有OOP语言都使用LSP) - 因此符合 - ICloseable ;然后选择按名称实现ICloseable接口。我不确定这里会发出什么警告。
如果C#在结构上(或鸭子)键入,则可以使用Closer代替ICloseable,但它不能; ICloseable c = new Closer()
无效,因为Closer未被定义为主要相关的ICloseable。
对我而言,C#和Java在这种情况下有些低级。
没有;除非在C#中谈论dynamic
。见上文。
有人可以解释一下背后的语言因素吗?由于C#和Java都以同样的方式执行,因此似乎有充分的理由。
这是通过语言设计选择;接口是支持单一继承模型中的命名类型的一种方法。 Scala(和Ruby)支持Traits; C ++支持多重继承。 Eiffel支持MI并且还在类型级别破坏LSP。去图 - 没有“正确的方法”。