【问题标题】:Is it possible to force an interface implementation to be virtual in C#?是否可以强制接口实现在 C# 中是虚拟的?
【发布时间】:2012-07-03 10:41:34
【问题描述】:

我今天遇到了一个问题,试图覆盖一个没有被声明为虚拟的接口方法的实现。

在这种情况下,我无法更改接口或基本实现,必须尝试其他方法,但我想知道是否有办法强制类使用虚拟方法实现接口。

例子:

interface IBuilder<T>
{
    // Already implicitly virtual
    /*virtual*/ T Build();
}

// This is a class written by someone else
class SimpleBuilder: IBuilder<SomeObject>
{
    // I would like to force this to be virtual
    public SomeObject Build() { return new SomeObject(); }
}

// This is the class I am writing.
class CustomBuilder: SimpleBuilder
{
    public /*override*/ SomeObject Build()
    {
        var obj = base.Build();
        obj.ModifyInSomeWay();
        return obj;
    }
}

编辑:CustomBuilder 旨在与 MEF 一起使用,因此我从 SimpleBuilder 派生,以便 MEF 解析正确的类型。我已经尝试显式实现该接口,而不是从 SimpleBuilder 派生,但 MEF 没有选择正确的类型。

有问题的接口和基类位于另一个开发人员创建的共享模块中,因此看来我无论如何都必须让他们更改其基类。

【问题讨论】:

  • 您可以在派生类上显式实现接口,但这只会影响通过接口进行的调用,而不影响通过派生类自己的类型(也不通过其基类)​​进行的调用。
  • 您能将SimpleBuilderBuild() 方法声明为抽象方法吗?
  • 我假设其他人是 SimpleBuilder 的作者,我无法轻易更改它。我希望接口强制将该方法声明为虚拟,以便稍后编写自定义版本时可以覆盖它。

标签: c# inheritance interface


【解决方案1】:

不,没有,在您的示例中,您是implicitly implementing 接口。如果您的类要被子类化并且方法被覆盖,则由开发人员确保该方法被标记为虚拟方法。

没有办法强制接口方法的实现在代码中是可覆盖的,只有开发团队内的约定可以确保这一点(例如,所有接口都应显式实现,所有接口实现都应标记为虚拟等)。

在这个特定示例中,您可以将抽象类插入到层次结构中:

abstract class VirtualBuilder<T> : IBuilder<T>
{
  abstract T Build();
}

但这不适用于通用情况,因为您失去了接口的好处,并且“强制”所有具体类只实现一个接口。

【讨论】:

    【解决方案2】:

    不直接,不。

    您可以通过提供自己的基类并要求用户从它派生来“强制”它:

    abstract class BuilderBase<T> : IBuilder<T>
    {
        public abstract T Build();
    }
    

    但这也有严重的问题

    1. 它不会立即阻止任何人直接实现接口(尽管您可以例如要求传入参数为BuilderBase&lt;T&gt; 而不是简单的IBuilder&lt;T&gt;,或者甚至使IBuilder&lt;T&gt; internal 并将其隐藏在另一个程序集中)
    2. 它迫使接口的实现者放弃他们对单个基类的选择,他们可能会讨厌你

    【讨论】:

      【解决方案3】:

      你不能强制它是虚拟的,但你可以使用 new 关键字;它被称为Method Hiding

      // This is the class I am writing. 
      class CustomBuilder: SimpleBuilder, IBuilder<T> 
      { 
          public new SomeObject Build() 
          { 
              var obj = base.Build(); 
              obj.ModifyInSomeWay(); 
              return obj; 
          } 
      } 
      

      【讨论】:

      • 这种方法有问题。
      • @leppie - 同意。我不经常使用它,但在这种情况下,它可能是必要的
      • new 在这种情况下不起作用,因为使用我的新类的代码正在通过接口访问它。
      • @andrews_nz - 你说“通过界面”??然后就可以了,只需实现接口并使用新的关键字即可。
      • @andrews_nz - 这里建议使用相同的方法:weblogs.sqlteam.com/mladenp/archive/2007/04/09/60168.aspx
      【解决方案4】:

      无法指定接口方法将如何实现。

      但是,即使基本实现不是虚拟的,您也有两种方法可以在子类上重新实现接口方法:

      • 使用new 方法限定符隐藏基本实现,或者
      • 这样的显式接口实现:

        T IBuilder.Build() { 变量 obj = 构建(); obj.ModifyInSomeWay(); 返回对象; }

      这些方法中的任何一种都将确保只有当您的类通过接口访问时才会调用您的方法,但如果调用使用基类引用则不会。

      所以我会说这个基类还没有准备好通过继承进行扩展。

      【讨论】:

        【解决方案5】:

        您不能这样做,因为接口只描述了类的外观,而没有说明实现本身。

        换句话说,如果你使用一个实现接口的类,该接口定义了一个方法Foo(),你可以肯定,无论实现如何,这个类都有这个方法。

        你可以做什么:

        public interface IBuilder<T>
        {
            T Build();
        }
        
        public abstract class BaseBuilder<T> : IBuilder<T>
        {
            public abstract T Build();
        }
        
        public class CustomBuilder : BaseBuilder<CustomBuilder>
        {
            public override CustomBuilder Build()
            {
                throw new NotImplementedException();
            }
        } 
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-07-16
          • 2012-03-02
          • 2011-10-28
          • 2015-12-23
          • 1970-01-01
          相关资源
          最近更新 更多