如何继承构造函数?

想象一下具有许多构造函数和一个虚方法的基类

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 中更新它吗?

有没有办法继承构造函数? 有没有办法鼓励代码重用?

191214 次浏览

是的,您必须实现对每个派生都有意义的构造函数,然后使用 base关键字将该构造函数指向适当的基类,或者使用 this关键字将一个构造函数指向同一类中的另一个构造函数。

如果编译器对继承构造函数做了假设,我们就不能正确地确定对象是如何实例化的。在大多数情况下,您应该考虑为什么有这么多的构造函数,并考虑在基类中将它们减少到只有一个或两个。然后,派生类可以使用诸如 null之类的常量值屏蔽其中的一些类,并且只通过其构造函数公开必要的类。

更新

在 C # 4中,您可以指定默认参数值,并使用命名参数使单个构造函数支持多个参数配置,而不是每个配置只有一个构造函数。

是的,你必须复制所有的387个构造函数。你可以通过重定向它们来做一些重用:

  public Bar(int i): base(i) {}
public Bar(int i, int j) : base(i, j) {}

但你只能做到这样了。

387个构造函数? ? 这是你的主要问题。这个怎么样?

public Foo(params int[] list) {...}

太多的构造函数是设计失败的标志。最好是构造函数少且能够设置属性的类。如果确实需要对属性进行控制,可以考虑使用相同命名空间中的工厂,并将属性设置器设置为内部的。让工厂决定如何实例化类并设置其属性。工厂可以使用一些方法,这些方法接受所需的任意多个参数,以便正确地配置对象。

问题不在于 Bar 和 Bah 必须复制387个构造函数,而在于 Foo 有387个构造函数。Foo 显然做了太多的事情-快速重构!另外,除非您有很好的理由在构造函数中设置值(如果您提供了无参数构造函数,那么您可能就没有设置值) ,否则我建议使用属性获取/设置。

不,您不需要将所有387个构造函数复制到 Bar 和 Bah。Bar 和 Bah 的构造函数可以是多少,也可以是少,与在 Foo 上定义的构造函数数量无关。例如,您可以选择只有一个 Bar 构造函数,它使用 Foo 的第212个构造函数构造 Foo。

是的,您在 Foo 中更改 Bar 或 Bah 所依赖的任何构造函数都将要求您相应地修改 Bar 和 Bah。

不,没办法进去。NET 来继承构造函数。但是您可以通过在子类的构造函数中调用基类的构造函数或通过调用您定义的虚方法(如 Initialize ())来实现代码重用。

你也许可以改编 C + + 虚构造函数习惯用法的一个版本。据我所知,C # 不支持协变返回类型。我相信这是很多人的愿望清单。

不要忘记,您还可以将构造函数重定向到同一继承级别的其他构造函数:

public Bar(int i, int j) : this(i) { ... }
^^^^^

由于 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() {...}
...
}

这不应该有一个更高的性能成本,除非你有很多默认属性值,你击中了很多。

可惜我们不得不告诉编译器一个显而易见的事实:

Subclass(): base() {}
Subclass(int x): base(x) {}
Subclass(int x,y): base(x,y) {}

我只需要在12个子类中做3个构造函数,所以这没什么大不了的,但是我不太喜欢在每个子类中重复这个过程,因为我已经习惯了不用写这么长时间的代码。我确信这是有正当理由的,但我认为我从未遇到过需要这种限制的问题。

public class BaseClass
{
public BaseClass(params int[] parameters)
{


}
}


public class ChildClass : BaseClass
{
public ChildClass(params int[] parameters)
: base(parameters)
{


}
}

我个人认为这是微软的一个错误,他们应该允许程序员覆盖构造函数、方法和属性在基类中的可见性,然后使构造函数始终是继承的。

这样,我们只是覆盖(较低的可见性-即。Private)我们想要的构造函数,而不是必须添加我们想要的所有构造函数。Delphi 就是这样,我很怀念。

例如,如果你想覆盖 System.IO.StreamWriter 类,你需要将所有的7个构造函数添加到你的新类中,如果你喜欢注释,你需要用头部 XML 注释每一个。更糟糕的是,元数据视图没有将 XML 注释作为正确的 XML 注释,因此我们必须逐行复制并粘贴它们。微软是怎么想的?

我实际上编写了一个小实用程序,您可以在其中粘贴元数据代码,它将使用覆盖可见性将其转换为 XML 注释。

另一个简单的解决方案是使用一个包含参数作为属性的结构或简单数据类; 这样你就可以提前设置所有的默认值和行为,将“参数类”作为单个构造函数参数传入:

public class FooParams
{
public int Size...
protected myCustomStruct _ReasonForLife ...
}
public class Foo
{
private FooParams _myParams;
public Foo(FooParams myParams)
{
_myParams = myParams;
}
}

这避免了多个构造函数(有时)的混乱,并提供了强类型、默认值以及参数数组所没有提供的其他好处。它还使继承变得容易,因为从 Foo 继承的任何东西仍然可以根据需要到 FooParams,甚至可以添加到 FooParams。您仍然需要复制构造函数,但是您总是(大多数时候)只(作为一般规则)永远(至少现在)需要一个构造函数。

public class Bar : Foo
{
public Bar(FooParams myParams) : base(myParams) {}
}

我非常喜欢重载的 Initailize ()和 Class Factory 模式方法,但是有时候您只需要一个智能构造函数。只是个想法。

我真的必须复制所有构造函数从 FooBarBah?如果我在 Foo中改变了构造函数签名,我是否需要在 BarBah中更新它?

是的,如果使用构造函数创建实例。

没有继承构造函数的方法吗?

没有。

有没有办法鼓励代码重用?

我不会讨论继承构造函数是好事还是坏事,也不会讨论继承构造函数是否会鼓励代码重用,因为我们没有构造函数,也不会得到构造函数。:-)

但是在2014年,使用当前的 C # ,您可以通过使用泛型 create方法来获得非常多的 喜欢继承构造函数。它可以是一个有用的工具,在你的腰带,但你不会轻易达到它。最近,当我需要向数百个派生类中使用的基类型的构造函数中传递一些内容时,我使用了它(直到最近,基类不需要任何参数,所以缺省构造函数很好 & mash,派生类根本没有声明构造函数,而是自动提供的构造函数)。

它看起来像这样:

// 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);
}
}
}