使用部分类实现分层接口以提高可读性

Cod*_*shi 3 c#

我知道我们可以使用部分类来分隔接口的实现,因此它更加有条理和可读性。

但是,如果我要实现的接口继承了其他接口,例如:

public interface IA
{
    void DoA();
}
public interface IB : IA
{
    void DoB();
}
public partial class B : IB
{
    public void DoB() { }
}
public partial class B : IA
{
    public void DoA() { }
}
Run Code Online (Sandbox Code Playgroud)

这与执行此操作相同:

public class B : IB, IA { // ... }
Run Code Online (Sandbox Code Playgroud)

编译器允许这样做,但是我不确定这是否会给我带来麻烦。有人可以澄清是否会引起问题。

我正在考虑改为执行此操作并使用注释,但是如果不引起任何问题,我宁愿采用上述方法:

public partial class B : IB
{
    public void DoB() { }
}
public partial class B // IA Implementation
{
    public void DoA() { }
}
Run Code Online (Sandbox Code Playgroud)

编辑

如果我在下面这样做,编译器会抱怨“'IA'已经在接口列表中列出了”。

public class B : IA, IA
{
}
Run Code Online (Sandbox Code Playgroud)

但这将允许:

public class B : IB, IA { // ... }
Run Code Online (Sandbox Code Playgroud)

如果IB继承,为什么会允许它IA。显然,这两种情况是不同的,因为编译器允许一种情况,而不允许另一种情况。一定是有原因的。

Eri*_*ert 9

编译器允许这样做,但是我不确定这是否会给我带来麻烦。有人可以澄清是否会引起问题。

在大多数情况下,这是一种合理的做法。我经常使用部分类来分离接口实现或嵌套类的实现,您应该对此充满信心。

但是,在一种情况下,您描述的显式调出已实现的接口的做法可能会让您感到惊讶,这就是接口重新实现规则

最好用一个小例子来描述。假设你有

interface I { void Foo(); }
// B does not implement I.
class B { public void Foo() {} }
class C : B, I { }
Run Code Online (Sandbox Code Playgroud)

那是完全合法的。C实现I,这意味着C必须具有方法Foo,而C确实具有方法Foo;它是从B继承而来的。

现在考虑:

// D implements I because D derives from C
class D : C { public new void Foo() {} }
// The I is unnecessary here, right? Or is it?
class E : D, I { }
Run Code Online (Sandbox Code Playgroud)

接口重新实现的规则是:因为E明确表示它实现了I,所以编译器将重新进行分析以确定Foo与I.Foo匹配的方法。加起来:

  • ((I)(new C())).Foo() 来电 B.Foo
  • ((I)(new D())).Foo() 来电 B.Foo
  • ((I)(new E())).Foo() 来电 D.Foo

这可能令人惊讶,并且在不必要地声明接口时可能会意外发生。它几乎永远不会发生,并且几乎什么时候都不会发生,但是您要求任何可能的问题,这就是这种情况。

现在让我们考虑您的后续问题:

显然,这两种情况是不同的,因为编译器允许一种情况,而不允许另一种情况。一定是有原因的。

正如我们已经看到的,C#的设计师想要一种表达“我已经引入了一个我想绑定到接口成员的新成员”的方式,而他们做出的模糊选择是允许这个多余的声明表示“立即重新绑定”。

但是,还有另一个设计原因是允许这种冗余,并且在发生冗余时不对其进行警告。

考虑奇异的C#设计决策时,一种好的做法是记住C#的设计人员始终时刻担心版本软件中的内容更改。他们尤其担心“脆性基类故障”的许多不同形式。那是失败模式,在此模式下,某人进行了对基类完全合理的更改,然后导致派生类意外中断或表现异常。

例如考虑该事件序列。首先,开发人员X写道:

interface IA { void Foo(); }
Run Code Online (Sandbox Code Playgroud)

然后开发者Y写道

class C { public void Foo() {} }
Run Code Online (Sandbox Code Playgroud)

然后开发人员Z写道:

class D : C, IA { }
Run Code Online (Sandbox Code Playgroud)

然后开发人员Y意识到,哦,我实施了IA的合同,我可以免费获得它,并将C类更改为

class C : IA { }
Run Code Online (Sandbox Code Playgroud)

因此,现在的问题是:编译器是否应在类D上给出冗余警告?答案是否定的,D类的作者没有做错任何事情,因此不应致力于解决实际上不存在的问题。

这就是C#倾向于容忍冗余的原因,尽管不幸的是,它并不总是能够容忍的。(例如,重述通用约束是非法的,但我认为应该允许。)

简而言之,任何时候您以为“ C#可能会告诉我有关此冗余的内容”时,请问自己这是否由其他人做出了我无法控制的更改导致?这会很烦人还是有帮助?如果令人讨厌,语言设计可能会故意抑制警告。