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

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

我有一个基类与构造函数需要一个参数:

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()会使编译器错误无效,但是提供了一种实例化FooBar的危险方法,而不需要初始化所需的基类int值。 因为我正在使用工厂(见下文),所以静默编译器错误只会导致稍后的运行时错误。 我想强制FooBar实现FooBar(int)

有趣的观察:

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

  1. Foo不inheritance默认构造函数,因为它提供了显式构造函数。
  2. FooBar DOESinheritance了FooBase()。

但是,非默认构造函数FooBase(int)也是如此!

  1. Foo必须显式实现FooBase(int)并调用base(int)。
  2. FooBar FAILS’inheritance’非默认构造函数的方式与inheritance默认构造函数的方式相同!

我不希望基类中有默认构造函数,因为实例是使用提供所需“settings”参数的工厂方法创建的。 此处未说明该工厂方法(使用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. } 

因为我使用的是工厂 – 而不是如图所示的直接实例化 – 所以没有编译器错误。 相反,我得到一个运行时exception:’找不到类型的构造函数。’

不可行的解决方案:

  • 接口不支持构造函数。
  • 构造函数不能是虚拟的或抽象的。

似乎提供基类不能强制构造函数的合同。

解决方法:

  • 在基类中提供默认构造函数以及要传递设置参数的属性。

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

这是不正确的 – 一般来说构造函数永远不会被inheritance。 为不提供任何其他构造函数实现的类自动提供默认构造函数。

您可以在为您提供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); } } 

所以这很安全。 但是,如果您尝试执行以下操作

 public class FooBase { protected int value; public FooBase() {} // <------------ LOOK HERE 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() // <----------------- No error here { } public override void DoSomething() { Console.WriteLine("FooBar: {0}", value); } } 

如果在FooBase中声明ctor是错误的,那么作为开发人员你不应该这样做......

如果只为构造函数提供基类的参数,则派生类在构造时必须调用它。 然而,这并不强迫它应该如何被调用。 可以使用某个默认值调用它,或者可以从其他构造函数参数计算该值。

构造函数不是inheritance的。 相反,当你没有在类中指定任何构造函数(结构以不同的方式行动)时,会创建一个公共无参数构造函数,它调用基类的无参数构造函数。 当然,如果基类没有这样的构造函数,就不会发生这种情况,在这种情况下你必须自己指定构造函数。

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

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

 interface IFoo { void DoSomething(); } 

因此,不同的实现需要不同的构造函数是很自然的,因为它们通常需要不同的内部状态来实现IFoo.DoSomething。

您遇到的问题是如何编写知道如何实例化所有这些不同类型的通用代码。 Activator.CreateInstance的工厂方法和变体通常用于以临时方式实现此目的。 我鼓励我们考虑一些可以更优雅地解决这个问题的方案:

  • IoC / DI容器通常是一种更好的方法,因为它们标准化了这些技术,性能更好,并带来了许多额外的function。
  • 在.NET 4中,Managed Extensibility Framework(MEF,又名System.ComponentModel.Composition)具有一组重叠的function,具有许多相同的优点,特别适用于插件设计。