为什么C#中的基类可以在不继承接口约定的情况下实现接口约定?

为什么C#中的基类可以在不继承接口约定的情况下实现接口约定?,c#,interface,base-class,C#,Interface,Base Class,我偶然发现了C#-的这个“特性”,实现接口方法的基类不必从中派生 例如: public interface IContract { void Func(); } // Note that Base does **not** derive from IContract public abstract class Base { public void Func() { Console.WriteLine("Base.Func"); } } // N

我偶然发现了C#-的这个“特性”,实现接口方法的基类不必从中派生

例如:

public interface IContract
{
    void Func();
}

// Note that Base does **not** derive from IContract
public abstract class Base
{
    public void Func()
    {
        Console.WriteLine("Base.Func");
    }
}

// Note that Derived does *not* provide implementation for IContract
public class Derived : Base, IContract
{
}
所发生的情况是,
Derived
神奇地选择了一个公共方法,
Base.Func
,并决定它将实现
IContract.Func

这种魔力背后的原因是什么


IMHO:这种“准实现”特性非常不直观,使代码检查更加困难。你觉得怎么样?

为什么你认为这很奇怪和不自然?基类的每个公共成员也是派生类的公共成员。所以这里没有矛盾。无论如何,如果愿意,您可以明确地实现接口。

原因是您的评论根本不正确:

//请注意,派生不提供IContract的实现

当然有。按照逻辑进行

  • “派生”必须提供与IContract的每个成员对应的公共成员
  • 基类的所有可继承成员也是派生类的成员;这就是继承的定义
  • 因此,派生为IContract提供了一个实现;其继承成员是满足要求的成员
  • 因此,没有错误
这个特性非常不直观,使代码检查更加困难。你觉得怎么样


我认为如果你不喜欢这个功能,就不应该使用它。如果您发现阅读使用此功能的代码令人困惑和奇怪,请鼓励使用此功能的同事停止这样做

这个特性与从派生类使用基类方法的任何其他特性有何不同?在派生类中使用或提及基类中的方法有许多不同的方式——方法调用、重写、方法组转换等等


此外,这相对来说是一个简单明了的案例。如果你真的想抱怨在C#中混淆接口语义,我会花时间抱怨接口重新实现语义。这是一个似乎真的烤人们的面条。我总是必须在规范中查找这些内容,以确保语义正确。

我“认为”可以通过在派生类中显式实现接口来解决副作用。这对我来说很有意义。当派生继承IContract时,这意味着它提供Func(),它就是这样做的。它通过继承来实现这一点只是偶然的。请注意,Base不实现IContract-如果您尝试将Base强制转换为IContract,它将失败。IMHO这种功能会给语言增加不必要的复杂性。简单而自然的方法是从IContract派生基,以显式地声明它实现了它。人类是简单的生物-我们无法立即解析所有代码并交叉引用继承图来理解IContract.Func和Base.Func是相关的(重命名IContract.Func one必须手动重命名Base.Func)。当这三个类都位于不同的项目/解决方案中时,它变得更加复杂。简而言之,这个特性是有害的,因为代码的可读性受到了极大的影响。程序员(可能)必须遍历每个基类才能找到接口实现,即使是通过与接口完全无关的基类也是如此。好的,我将尝试解释一下:您试图描述的情况是罕见的和不自然的。尤其是这个“当所有三个类都位于不同的项目/解决方案中时,它变得更加复杂”。同样,您可以显式地实现接口,以避免与基类方法发生冲突。@etarassov:假设您没有实现基类,但实现了派生类。那么你就不能让它实现你想要的接口。假设您想要实现派生和其他派生,并且希望其他派生不实现契约。在这两种情况下,都不能将接口添加到基。因此,必须提出一些不需要将接口添加到基础的机制。@Eric Lippert:好的观点。但我仍然倾向于认为,在这两种(角落?)情况下,组合应该优先于继承,因为基、派生和最终其他派生之间所需关系的复杂性。而且很多人建议你更喜欢组合而不是继承(一般来说)。谢谢你冗长的回答!我确实理解它是如何工作的,但我认为它的工作方式是违反直觉的,原因有很多:(1)一个人先看价格标签,然后付款,就像一个人先声明,然后才实施smth一样。(2) 派生是IContract,不实现它,但使用Base来实现IContract,它本身不是IContract(3),通过读取Base的代码无法确定Base.Func是否与IContract.Func(关系declare/implement)相关。(4) 更改Base.Func将产生编译错误,甚至不会提到Base“我认为如果您不喜欢它,就不应该使用该功能。”-100%同意。不幸的是,其他人可能会使用它(或现有的代码库),这将使代码难以阅读/理解。真正伟大的语言指导开发人员编写好的代码(理想情况下会强制编写)。C#(一种非常好的语言)只有在删除此功能后才会受益。但这将是非常困难和痛苦的。。。与使用循环变量的foreach内部的闭包完全相同。无论如何,感谢您提供“接口重新实现语义”的链接!