显式接口实现不能是虚拟的。

17

记录一下,我已经看到了这个连接项,但我实在无法理解为什么不支持这种方式。

假设我有以下代码:

public interface IInterface
{
    void Method();
}

public class Base : IInterface
{
    virtual void IInterface.Method()
    {
        throw new NotImplementedException();
    }
}

虚标识符有什么问题?如果有一个虚拟修饰符,就可以使用override关键字表示在基类中有不同的实现。现在,我可以通过删除虚方法并像这样创建派生类来解决这个问题:

public class Derived : IInterface
{
    void IInterface.Method()
    {
        throw new NotImplementedException();
    }
}
然而,按照我这种方式,我完全没有任何迹象表明我正在覆盖某些内容。
更新:根据C#规范 (部分:20.4.1 显式接口成员实现),有两个原因。
1. 隐藏某些方法(我用它来做这个)。 2. 有两个具有相同签名但返回类型不同的函数(例如,对于IClonable很有用)。
然而,它并没有说明为什么不能将这些方法设置为虚拟的。
更新2:根据答案,我认为我应该重新表述真正的问题。如果上述两个原因是为什么首先使显式接口实现成为可能的原因。那么,如果您将方法设置为虚拟的,会有什么问题?
5个回答

16

显式实现接口的方法具有特殊的可见性范围 = 除非将“this”强制转换为目标接口类型,否则无法从另一个方法访问它。 我想这就是为什么不支持虚拟关键字的原因 - 您不能覆盖不属于正常对象接口(私有/受保护/公共)的方法。

这是我的解决方法:

public class Base : IInterface
{    
   protected virtual void Method()
   {

   }

   void IInterface.Method()    
   {        
       this.Method()
   }
 }


 public class Derived : Base
 {
     protected override void Method()
     {
     }
 }

我不想在基础类型上拥有该方法。这样它就会在不属于“IInterface”类型的实例中变得可见。显式实现的一个好处是当类没有通过接口访问时,可以隐藏该方法。 - thekip
2
如果将其声明为“protected”,则只有派生类才能看到它。因此,您既具有私有接口实现,又具有可扩展性以供派生类使用。 - alexm
3
所有继承自“Base”的类都是IInterface类型,您只能替换实现方式。 - alexm
在这种情况下,使用的确切方法与Albahari的《C# 5.0 in a Nutshell》建议的方法完全相同。 - Burak Karakuş
这应该是目前为止最好的答案。将方法更改为“protected virtual void InnerMethod()”可能是一个更好的名称。 - lingtianlan

7
然而,这种方式并不能明确表明我正在覆盖某些东西。
嗯,有点道理——你可以看出它显然是一个显式接口实现。这表明它为在接口中指定的方法调用提供了多态行为……那么基类是否还实现接口有什么关系呢?当你阅读代码时,这会对你产生什么影响吗?
对我来说,声明 override 的主要好处是确保我真正拥有正确的签名——它匹配我尝试覆盖的东西。通过显式接口实现,如果您给出了不存在的方法或错误的参数等,编译器已经会报错,因此您已经获得了该好处。
我有点能够理解你的观点,但我从未发现过它真正成为问题。

对我来说,这也告诉我,如果我不调用基本实现,我可能会替换基类的功能。我的主要“问题”是(对我来说)为普通方法覆盖一个重载并且无法对显式实现执行相同操作感到尴尬。我希望这能够以相同的方式实现。 - thekip
1
@thekip:基本上,显式接口实现在很多方面都有些奇怪。除非你真的需要使用显式接口实现,或者你试图积极隐藏一个根本不应该被调用的方法(例如,在不可变类型上进行突变的方法),否则我建议你坚持使用隐式接口实现。请记住,如果使用显式接口实现,你的派生类方法甚至不能先调用基类方法。 - Jon Skeet
1
嗯,当我在派生类中重写一个显式接口方法时,我知道我的基类也实现了相同的接口,因此我可以安全地将我的基类转换为IInterface并调用基类方法。C#规范给出了两个使用显式实现的原因(请参见问题),但我看不出为什么虚拟部分是无效的。 - thekip
2
@thekip:你说的“将我的基础转换为IInterface”是什么意思?你只有“this”,如果你将那个引用强制转换为IInterface,你最终会再次调用自己。(如果你有一种调用基础实现的方法,我很感兴趣。) - Jon Skeet
1
没关系,我看到了在实现显式接口时的另一个限制。这可能就是为什么没有必要重写(因此指定虚拟)的原因,因为你不能真正地重写一个函数,只能完全替换它。 - thekip
@thekip:确切地说,虚拟显式实现的概念并不适用。 - Jon Skeet

0

将显式接口实现变为虚拟的唯一有用场景是:当派生类重写需要调用父类实现时。不幸的是,即使可以将显式接口实现变为虚拟,除非有一些新的语法来实现这样做,否则覆盖类无法调用其父类的实现。VB.net通过允许实现接口的方法使用与接口方法不同的名称来声明Protected方法来很好地处理这个问题。因此,派生类可以重写Protected方法(使用适当的名称),并且该重写可以调用父类版本(使用相同的名称)。


在我看来,你似乎是说显式接口实现不能是 virtual 的,因为它不能是 override :-p。如果我使用显式接口实现的唯一原因是避免当前类中的命名空间冲突,那么它支持像普通方法一样的 virtualoverride 似乎很自然。所以我想VB方法听起来不错... - binki
1
@binki:在我看来,VB的方法是 ".NET" 正确的方法。它允许解决命名空间冲突问题,使子方法能够正确地链接到它们的父方法,而无需创建一个仅用于调用虚拟方法的接口实现。如果 .NET 允许一种类型同时覆盖和隐藏一个方法,或者允许私有虚函数(可以被重写,但只能从派生实现内部进行链接),那么也可能有其他方法可行。 - supercat

-1
如果只有一个接口被继承,为什么还需要这样做呢:

public class Base : IInterface
{
    virtual void IInterface.Method()
    {
       throw new NotImplementedException();
    }
}

为什么不直接这样写:

public class Base : IInterface
{
   virtual void Method()
   {
      throw new NotImplementedException();
   }
}

也许这只是他向我们展示的一个示例。如果假设可能会有另一个名为Method()的自定义方法。 - Zenwalker
1
因为该方法仅在接口上下文中相关。当您不处理接口实例时,无需调用该方法。 - thekip

-1

我认为原因可以通过以下示例简单地展示。考虑以下代码:

public interface IInterfaceA
{
    void Method();
}

public interface IInterfaceB
{
    void Method();
}

public class Base : IInterfaceA, IInterfaceB
{
    virtual void IInterfaceA.Method()
    {
       ...
    }

    virtual void IInterfaceB.Method()
    {
       ...
    }
}

public class Derived : Base
{
    public override void Method()
    {
        // Will this override IInterfaceA or IInterfaceB implementation???
    }
}

所以,简而言之,如果您明确实现多个具有相同方法签名的接口,则派生类将不知道要覆盖哪个基础方法。


1
那么关于 - 覆盖 void IInterfaceA.Method() ..呢?那就可以了。顺便说一下,你不能在显式接口实现上指定 public。它默认是 public 的。 - Rashack
这个回答似乎只是说“无法完成,因为没有相应的语法”。我们想知道为什么不支持它(包括用于消除override/base引用歧义的语法),而不是重申这一点(我认为)。 - binki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接