3

C# 中的接口方法可以显式实现,以便在将实例显式转换为接口类型时调用它们的实现。为什么类的虚拟方法也不支持这一点?

尽管解决“多重继承”问题对于接口来说是独一无二的,但似乎由于显式实现的成员对接口有用的所有其他原因,它们对虚拟方法也很有用。一个更清晰的回报型协方差模型浮现在脑海中。

编辑:根据要求,一个例子:

public class Foo {
    ...
}

public class Bar : Foo {
   ...
}

class Base {
   abstract Foo A ();
}

class Dervied {
  private Bar _b;

  Bar A () {
    return _b;
  }

  Foo Base.A () {
    return _b;
  }
}

我知道使用辅助方法来模拟这一点,但最终效果似乎具有显式实现所具有的任何不良特征,但 API 更脏。我的问题的症结不是如何进行返回类型协方差,而是为什么虚拟方法不支持类似的接口机制。

4

2 回答 2

5

有些人建议一开始就没有public virtual方法。而是创建一个public代表消费者接口的非虚拟方法和一个protected virtual代表实现者接口的方法。

我不会将调用者和实施者的合同分开称为“混淆设计”。在许多情况下,它更清洁 IMO,但我通常懒得实际这样做。

这种设计在返回类型协方差和方法隐藏方面效果更好。

这样做的另一个好处是public包装器可以添加额外的检查代码并支持调用者和实现者的不同合约。

我如何模拟返回类型协方差的一个例子:

public class Base
{
    protected virtual Base FooOverride(int i){return new Base();};//FooOverride does not need to duplicate the argument checking

    public Base Foo(int i)
    {
        if(i<0)
          throw new ArgumentException("i<0");
        return FooOverride(i);
    }
}

public class Derived:Base
{
    protected override Base FooOverride(int i){return new Derived();};
    public new Derived Foo(int i)
    {
        return (Derived)base.Foo();
    }
}
于 2011-07-03T16:19:10.250 回答
3

除了允许这样的事情之外,这还有什么好处?

class Base
{
    virtual void M() { }
}

class Derived : Base
{
    override void M() { }

    override void Base.M() { }
}

这有效地将违反Liskov 替换原则的行为融入到 C# 语言中——如果我有一个 Base 类型的变量,根据运行时类型是 Base 还是 Derived,对其调用 M() 可以做完全不同的事情。

显式接口实现是不同的。假设你有这个:

interface IFoo
{
    void DoStuff();   
}

interface IBar
{
    void DoStuff();
}

class C : IFoo, IBar
{
    void IFoo.DoStuff() { }

    void IBar.DoStuff() { }
}

这保留了 LSP - 如果我有一个恰好是运行时类型 C 的 IFoo 变量,则在其上调用 DoStuff() 将获得它的 IFoo 实现。IBar 也是如此。

于 2011-07-03T16:07:41.280 回答