C# 如何继承构造函数?

C# 如何继承构造函数?,c#,inheritance,constructor,C#,Inheritance,Constructor,想象一下一个包含许多构造函数和虚拟方法的基类 public class Foo { ... public Foo() {...} public Foo(int i) {...} ... public virtual void SomethingElse() {...} ... } 现在我想创建一个覆盖虚拟方法的子类: public class Bar : Foo { public override void SomethingElse() {...}

想象一下一个包含许多构造函数和虚拟方法的基类

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


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

是的,您必须实现对每个派生都有意义的构造函数,然后使用
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

public class Bar : Foo
{
    public Bar(FooParams myParams) : base(myParams) {}
}
是的,您在Bar或Bah依赖的Foo中更改的任何构造函数都将要求您相应地修改Bar和Bah


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

您可以调整。据我所知,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)我们不想要的构造函数,而不必添加我们想要的所有构造函数。德尔福就是这样做的,我很怀念

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


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

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

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()和类工厂模式,但有时您只需要有一个智能构造函数。只是一个想法

我真的必须这么做吗
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);
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);
        }
    }
}