想象 一个包含许多构造函数和虚方法的基类
public class Foo
{
...
public Foo() {...}
public Foo(int i) {...}
...
public virtual void SomethingElse() {...}
...
}
现在我想创建一个覆盖虚方法的后代类:
public class Bar : Foo
{
public override void SomethingElse() {...}
}
另一个后代做了更多的东西:
public class Bah : Bar
{
public void DoMoreStuff() {...}
}
我是否真的必须将所有构造函数从Foo复制到Bar和Bah?然后,如果我在Foo中更改构造函数签名,我是否必须在Bar和Bah中更新它?
有没有办法继承构造函数?有没有办法鼓励代码重用?
答案 0 :(得分:122)
是的,您必须实现对每个派生有意义的构造函数,然后使用base
关键字将该构造函数指向适当的基类或this
关键字以指示构造函数同一个类中的另一个构造函数。
如果编译器假设继承构造函数,我们将无法正确确定对象的实例化方式。在大多数情况下,您应该考虑为什么有这么多构造函数并考虑将它们减少到基类中的一个或两个。然后派生类可以使用null
之类的常量值来掩盖其中的一些,并且只通过它们的构造函数公开必要的值。
在C#4中,您可以指定默认参数值并使用命名参数使单个构造函数支持多个参数配置,而不是每个配置都有一个构造函数。
答案 1 :(得分:70)
387个构造函数?那是你的主要问题。怎么样呢?
public Foo(params int[] list) {...}
答案 2 :(得分:42)
是的,您必须复制所有387个构造函数。您可以通过重定向来重复使用它们:
public Bar(int i): base(i) {}
public Bar(int i, int j) : base(i, j) {}
但这是你能做的最好的事情。
答案 3 :(得分:34)
太糟糕了,我们被迫告诉编译器显而易见:
Subclass(): base() {}
Subclass(int x): base(x) {}
Subclass(int x,y): base(x,y) {}
我只需要在12个子类中做3个构造函数,所以这没什么大不了的,但我不太喜欢在每个子类上重复这个,因为我不习惯这么长时间写它。我确信这是有道理的,但我认为我从未遇到过需要这种限制的问题。
答案 4 :(得分:27)
不要忘记您也可以在相同的继承级别将构造函数重定向到其他构造函数:
public Bar(int i, int j) : this(i) { ... }
^^^^^
答案 5 :(得分:10)
另一个简单的解决方案可能是使用包含参数作为属性的结构或简单数据类;这样你可以提前设置所有默认值和行为,将“参数类”作为单个构造函数参数传递:
public class FooParams
{
public int Size...
protected myCustomStruct _ReasonForLife ...
}
public class Foo
{
private FooParams _myParams;
public Foo(FooParams myParams)
{
_myParams = myParams;
}
}
这避免了多个构造函数(有时)的混乱,并提供了强类型,默认值以及参数数组未提供的其他好处。它还可以很容易地继续发展,因为从Foo继承的任何内容仍然可以根据需要访问甚至添加到FooParams。你仍然需要复制构造函数,但你总是(大部分时间)只(作为一般规则)(至少现在)需要一个构造函数。
public class Bar : Foo
{
public Bar(FooParams myParams) : base(myParams) {}
}
我非常喜欢重载的Initailize()和类工厂模式方法更好,但有时你只需要一个智能构造函数。只是一个想法。
答案 6 :(得分:7)
由于Foo
是一个类,您是否可以创建虚拟重载Initialise()
方法?那么它们可用于子类并且仍可扩展吗?
public class Foo
{
...
public Foo() {...}
public virtual void Initialise(int i) {...}
public virtual void Initialise(int i, int i) {...}
public virtual void Initialise(int i, int i, int i) {...}
...
public virtual void Initialise(int i, int i, ..., int i) {...}
...
public virtual void SomethingElse() {...}
...
}
除非你有很多默认属性值并且你经常遇到它,否则这个性能成本不会更高。
答案 7 :(得分:6)
public class BaseClass
{
public BaseClass(params int[] parameters)
{
}
}
public class ChildClass : BaseClass
{
public ChildClass(params int[] parameters)
: base(parameters)
{
}
}
答案 8 :(得分:5)
我个人认为这在Microsofts部分是一个错误,它们应该允许程序员覆盖基类中的构造函数,方法和属性的可见性,然后使它成为总是继承构造函数。
这样我们只是简单地覆盖(具有较低的可见性 - 即私有)我们不想要的构造函数,而不必添加我们想要的所有构造函数。德尔福就是这样做的,我很想念它。
例如,如果要覆盖System.IO.StreamWriter类,则需要将所有7个构造函数添加到新类中,如果您想要注释,则需要使用标题XML对每个构造函数进行注释。更糟糕的是,元数据视图dosnt将XML注释作为正确的XML注释,因此我们必须逐行进行复制并粘贴它们。微软在这里想什么?
我实际上已经编写了一个小实用程序,您可以粘贴元数据代码,并使用覆盖的可见性将其转换为XML注释。
答案 9 :(得分:4)
我是否真的必须将
Foo
的所有构造函数复制到Bar
和Bah
?然后,如果我在Foo
中更改构造函数签名,是否必须在Bar
和Bah
中更新它?
是的,如果您使用构造函数来创建实例。
没有办法继承构造函数吗?
不。
有没有办法鼓励代码重用?
好吧,我不会深入了解继承构造函数是好还是坏,以及它是否会鼓励代码重用,因为我们没有它们而我们也不会得到它们。 : - )
但是在2014年,使用当前的C#,您可以通过使用通用create
方法获得非常类似的继承构造函数。它可以是一个有用的工具,但你不会轻易达到它。我最近在面对需要将一些东西传递给在几百个派生类中使用的基类型的构造函数时(最近,基类不需要任何参数,因此默认构造函数很好 - 派生的)类根本没有声明构造函数,并且获得了自动提供的构造函数。
看起来像这样:
// In Foo:
public T create<T>(int i) where: where T : Foo, new() {
T obj = new T();
// Do whatever you would do with `i` in `Foo(i)` here, for instance,
// if you save it as a data member; `obj.dataMember = i;`
return obj;
}
这表示您可以使用类型参数调用泛型create
函数,该参数是具有零参数构造函数的Foo
的任何子类型。
然后,您不必执行Bar b new Bar(42)
,而是执行此操作:
var b = Foo.create<Bar>(42);
// or
Bar b = Foo.create<Bar>(42);
// or
var b = Bar.create<Bar>(42); // But you still need the <Bar> bit
// or
Bar b = Bar.create<Bar>(42);
我已经在create
上直接显示了Foo
方法,但当然它可能在某种工厂类中,如果它所设置的信息可以由该工厂设置类。
为了清楚起见:名称create
并不重要,可能是makeThingy
或您喜欢的任何其他内容。
完整示例
using System.IO;
using System;
class Program
{
static void Main()
{
Bar b1 = Foo.create<Bar>(42);
b1.ShowDataMember("b1");
Bar b2 = Bar.create<Bar>(43); // Just to show `Foo.create` vs. `Bar.create` doesn't matter
b2.ShowDataMember("b2");
}
class Foo
{
public int DataMember { get; private set; }
public static T create<T>(int i) where T: Foo, new()
{
T obj = new T();
obj.DataMember = i;
return obj;
}
}
class Bar : Foo
{
public void ShowDataMember(string prefix)
{
Console.WriteLine(prefix + ".DataMember = " + this.DataMember);
}
}
}
答案 10 :(得分:3)
问题不在于Bar和Bah必须复制387个构造函数,问题是Foo有387个构造函数。 Foo显然做了太多事 - 快速重构!另外,除非你有一个很好的理由在构造函数中设置值(如果你提供无参数构造函数,你可能没有),我建议使用属性获取/设置。
答案 11 :(得分:2)
不,您不需要将所有387个构造函数复制到Bar和Bah。 Bar和Bah可以拥有任意数量的构造函数,而不依赖于您在Foo上定义的数量。例如,你可以选择只有一个Bar构造函数,用Foo的第212个构造函数构造Foo。
是的,您在Bar或Bah所依赖的Foo中更改的任何构造函数都需要您相应地修改Bar和Bah。
不,.NET中没有办法继承构造函数。但是您可以通过在子类的构造函数中调用基类的构造函数或通过调用您定义的虚方法(如Initialize())来实现代码重用。
答案 12 :(得分:1)
您可以调整C++ virtual constructor idiom的版本。据我所知,C#不支持协变返回类型。我相信这是许多人的愿望清单。
答案 13 :(得分:0)
太多的构造函数是设计损坏的标志。更好的是具有少量构造函数和设置属性的能力的类。如果您确实需要控制属性,请考虑同一命名空间中的工厂并使属性设置器处于内部。让工厂决定如何实例化类并设置其属性。工厂可以使用尽可能多的参数来正确配置对象。