Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/276.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用分部类实现分层接口以提高可读性_C# - Fatal编程技术网

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

C# 使用分部类实现分层接口以提高可读性,c#,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 Do

我知道我们可以使用分部类来分离接口的实现,因此它更具组织性和可读性

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

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() { }
}
public class C : B, IA
{
    void IA.DoA() { Console.WriteLine("DoA in C."); }
}
这与这样做是一样的:

public class B : IB, IA { // ... }
编译器允许这样做,但我不确定这是否会给我带来麻烦。有人能澄清一下这是否会引起问题吗

我正考虑这样做,并发表评论,但如果不会引起任何问题,我宁愿采用上述方法:

public partial class B : IB
{
    public void DoB() { }
}
public partial class B // IA Implementation
{
    public void DoA() { }
}
编辑

如果我做了以下操作,编译器将抱怨“'IA'已经列在接口列表中。”:

但它将允许:

public class B : IB, IA { // ... }

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

没有问题。部分课程是为了您的方便。它们不会影响生成的代码。如果您可以编写一个非分部类,那么就可以将其拆分为任意数量的分部类,而不会改变其行为

编译器不允许您在已实现接口列表中两次列出完全相同的接口,因为这将是愚蠢的,并且通常意味着您犯了错误(意味着一件事,但编写了另一件事)。但这种行为只是为了帮助您避免出错,因此编译器只验证接口在列表中的名称是否完全相同。如果该检查通过,则编译器将实现的接口视为一组唯一的元素,而不管通过不同的继承路径添加了多少次接口

假设您有IA扩展IDisposable和IB扩展IDisposable,并且您的类同时实现IA和IB。编译器将您的类视为实现IA、IB和IDisposable,就好像它们本身没有扩展任何其他接口一样

<>注意,C++不是C++,后者没有接口。在C++代码中,等价于C类的继承和接口实现将用虚拟继承来近似。但现代Pascal以及IEC-61131-3的CoDeSys-3扩展提供了语义与C#中类似的接口


Hans关于停止调用接口实现“继承”的警告将被忽略,后果自负。一种是在实现接口时不继承任何内容。接口没有数据,没有实现,它只是声明实现类需要提供哪些方法和属性。接口本质上是类的用户和类的作者之间的契约。继承不止于此。继承是具体的,它意味着您接受一个类类型并向其添加更多内容。接口实现不是这样的:你没有任何东西,你只是声明这里有一个实现了什么方法和属性的列表,这些列表就像契约:一个以“接口”类型作为输入的方法并不是真正要求接口。它要求一些具体的对象来履行接口的合同。

我对此进行了更多的思考,并满怀信心地得出结论,它不会引起任何问题。例如,为了简洁起见,如果我们将
partial
的概念删除一段时间,以便我们可以集中精力解决手头的问题,如果我们执行以下操作:

public class B : IB, IA
{
    public void DoA() { }
    public void DoB() { }
}
C#编译器不会抱怨,因为即使我们不必要地指出
B
实现了
IA
,它仍然是正确的。我们在重复自己,但这仍然是事实。也许编译器仍然应该警告我们“IA”已经列在接口列表或其他一些警告中,但它没有

因此,重复该类再次实现接口的事实,不会改变任何内容,也不会导致任何问题

话虽如此,如果在派生类中执行此操作,我们必须小心


编译器将不允许以下操作,并抱怨它隐藏了继承的成员,如果要隐藏,则使用
new
关键字

但是,如果我们显式地实现如下接口,这是我们必须小心的部分:

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() { }
}
public class C : B, IA
{
    void IA.DoA() { Console.WriteLine("DoA in C."); }
}
它将允许它,现在我们已经覆盖了
B
中指定的功能

下面是完整的代码来演示我所说的内容

public interface IA
{
    void DoA();
}
public interface IB : IA
{
    void DoB();
}
public class B : IB, IA
{
    public void DoA() { Console.WriteLine("DoA in B."); }
    public void DoB() { Console.WriteLine("DoB in B."); }
}

public class C : B, IA
{
    void IA.DoA() { Console.WriteLine("DoA in C."); }
}
还有一个快速测试:

var b = new B();
b.DoA();
b.DoB();

var c = new C();
c.DoA();           // Prints: DoA in B.
(c as IA).DoA();   // Prints: DoA in C.

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

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

但是,有一种情况,您可能会对所描述的显式调用已实现接口的实践感到惊讶,这就是接口重新实现规则

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

interface I { void Foo(); }
// B does not implement I.
class B { public void Foo() {} }
class C : B, I { }
这是完全合法的。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 { }
接口重新实现规则是:因为E显式地表示它实现了I,所以编译器重新进行分析以确定哪个方法Foo与I.Foo匹配。总结:

  • ((I)(新C()).Foo()调用
    B.Foo
  • ((I)(新的D()).Foo()
    调用
    B.Foo
  • ((I)(新的E()).Foo()
    调用
    D.Foo
这可能会令人惊讶,当您不必要地声明接口时,可能会意外发生。它几乎从未发生过,当它发生的时候也几乎不重要,但是你问了任何可能的问题,这就是这种情况

现在让我们考虑一下你。
interface IA { void Foo(); }
class C { public void Foo() {} }
class D : C, IA { }
class C : IA { }