C# 强制派生类使用参数实现基类构造函数

C# 强制派生类使用参数实现基类构造函数,c#,.net,C#,.net,是否可以对需要实现构造函数(带参数)的派生类强制执行编译时约定? 我有一个基类,它的构造函数需要一个参数: public class FooBase { protected int value; public FooBase(int value) { this.value = value; } public virtual void DoSomething() { throw new NotImplementedException(); } } 我想强制基类的派生实现相同的构造函数

是否可以对需要实现构造函数(带参数)的派生类强制执行编译时约定?

我有一个基类,它的构造函数需要一个参数:

public class FooBase
{
  protected int value;
  public FooBase(int value) { this.value = value; }
  public virtual void DoSomething() { throw new NotImplementedException(); }
}
我想强制基类的派生实现相同的构造函数:

public class Foo : FooBase
{
  public Foo(int value) : base(value) { }
  public override void DoSomething() { Console.WriteLine("Foo: {0}", value); }
}
如果未实现构造函数,则派生类会导致编译器错误,因为基类中没有默认构造函数:

// ERROR: 'Does not contain a constructor that takes 0 arguments'
// Adding default constructor in FooBase eliminates this compiler error, but
// provides a means to instantiate the class without initializing the int value.
public class FooBar : FooBase
{
  public override void DoSomething() { Console.WriteLine("FooBar: {0}", value); }
}
在派生类中添加默认构造函数FooBar()可以使编译器错误静音,但提供了一种危险的方法,可以在不初始化所需基类int值的情况下实例化FooBar。因为我使用的是工厂(见下文),所以沉默编译器错误只会在以后导致运行时错误。我想强制FooBar实现FooBar(int)

有趣的观察:

如果将默认构造函数FooBase()添加到FooBase,则它将由不提供构造函数的派生类“继承”:

  • Foo不继承默认构造函数,因为它提供显式构造函数
  • FooBar确实继承了FooBase()
  • 但是,对于非默认构造函数FooBase(int),情况并非如此

  • Foo必须显式实现FooBase(int)和callbase(int)
  • FooBar无法像继承默认构造函数一样“继承”非默认构造函数 我不希望在基类中使用默认构造函数,因为实例是使用提供所需“设置”参数的工厂方法创建的。这里没有说明工厂方法(它使用Activator.CreateInstance()方法)

    下面是应实例化派生类的方法:

      static void Main(string[] args)
      {
        FooBase myFoo = new Foo(4);     // Works, since Foo(int) is implemented.
    
        // ERROR: 'Does not contain a constructor that takes 1 arguments'
        FooBase myFooBar = new FooBar(9);  // Fails to compile.
      }
    
    因为我使用的是工厂,而不是如图所示的直接实例化,所以没有编译器错误。相反,我得到一个运行时异常:“找不到类型上的构造函数。”

    不可行的解决方案:

    • 接口不支持构造函数
    • 构造函数不能是虚拟的或抽象的
    似乎提供基类无法在构造函数上强制约定

    解决办法:

    • 在基类中提供默认构造函数以及传递设置参数的属性
    如果是默认构造函数FooBase(), 添加到FooBase,则 由执行以下操作的派生类“继承” 不提供构造函数:

    public class Foo : FooBase
    {
      public Foo(int value) : base(value) { }
      public override void DoSomething() { Console.WriteLine("Foo: {0}", value); }
    }
    
    这是不正确的-构造函数通常不会被继承。默认构造函数会自动为不提供任何其他构造函数实现的类提供

    您可以在为您提供Init()方法的接口上添加约束:

    public interface IInit
    {
       void Init(int someValue);
    }
    
    public class FooBase : IInit
    {
       ..
    }
    
    你试过了吗

    public class FooBase
    {
      protected int value;
      private FooBase(){}
      public FooBase(int value) { this.value = value; }
      public virtual void DoSomething() { throw new NotImplementedException(); }
    }
    

    私有构造函数阻止了无参数构造函数的选项。似乎我不明白你的意思,或者你的意思是错误的

    出现以下情况会导致编译器错误:

    public class FooBase
    {
        protected int value;
        public FooBase(int value) { this.value = value; }
        public virtual void DoSomething() { throw new NotImplementedException(); }
    }
    public class Foo : FooBase
    {
        public Foo(int value) : base(value) { }
        public override void DoSomething() { Console.WriteLine("Foo: {0}", value); }
    }
    
    public class FooBar : FooBase
    {
        public FooBar() // <----------------- HERE telling 'Test.FooBase' does not contain a constructor that takes 0 arguments
        {
        }
    
        public override void DoSomething() { Console.WriteLine("FooBar: {0}", value); }
    }
    
    公共类FooBase
    {
    保护整数值;
    公共FooBase(int值){this.value=value;}
    公共虚拟void DoSomething(){throw new NotImplementedException();}
    }
    公共类Foo:FooBase
    {
    公共Foo(int值):基(值){}
    public override void DoSomething(){Console.WriteLine(“Foo:{0}”,value);}
    }
    公共类FooBar:FooBase
    {
    
    public FooBar()//如果只为构造函数提供基类上的参数,则派生类在构造时必须调用该参数。但是,这并不强制它如何调用。可以使用一些默认值调用它,也可以从其他构造函数参数计算值

    构造函数不是继承的。相反,当您在类中不指定任何构造函数时,会发生什么情况(结构的行为不同),将创建一个公共无参数构造函数,该构造函数调用基类的无参数构造函数。当然,如果基类没有此类构造函数,则不会发生这种情况,在这种情况下,您必须自己指定构造函数


    正如@BrokenGlass所提到的,强制这样一个约束的唯一方法是在基类上有一个抽象方法
    Init()
    (可能来自接口)。我认为一般来说,这种做法不是好的OOP设计(对象应该在创建后可用,而不需要调用其他方法),但在这种情况下,它可能是最好的解决方案。

    正如其他人所指出的,接口方法,如Init()有点尴尬。接口应该公开行为,而不是内部需求。对象的内部状态有助于实现该行为。类通常是包装状态和行为的一种方式。构造函数公开内部需求,但接口“服务”的使用者不关心这个;他们只关心行为:

    interface IFoo 
    {
        void DoSomething();
    }
    
    所以很自然,不同的实现需要不同的构造函数,因为它们通常需要不同的内部状态来实现IFoo.DoSomething

    然后,您遇到的问题是如何编写通用代码,知道如何实例化所有这些不同类型。通常使用Activator.CreateInstance上的工厂方法和变体以特殊方式完成此任务。我鼓励您考虑一些更优雅地解决此问题的备选方案:

    • IoC/DI容器通常是一种更好的方法,因为它们标准化了这些技术,性能更好,并带来了许多附加功能
    • 在.NET4中,托管可扩展性框架(MEF,也称为System.ComponentModel.Composition)具有一组重叠的功能,具有许多相同的优点,特别适合插件设计

    正确。这就是为什么我在“继承”周围加上单引号的原因,目的是将其表示为对该术语的错误使用。其效果是基本构造函数