ChatGPT解决这个技术问题 Extra ChatGPT

How to inherit constructors?

Imagine a base class with many constructors and a virtual method

public class Foo
{
   ...
   public Foo() {...}
   public Foo(int i) {...}
   ...
   public virtual void SomethingElse() {...}
   ...
}

and now i want to create a descendant class that overrides the virtual method:

public class Bar : Foo 
{
   public override void SomethingElse() {...}
}

And another descendant that does some more stuff:

public class Bah : Bar
{
   public void DoMoreStuff() {...}
}

Do i really have to copy all constructors from Foo into Bar and Bah? And then if i change a constructor signature in Foo, do i have to update it in Bar and Bah?

Is there no way to inherit constructors? Is there no way to encourage code reuse?


C
Community

Yes, you will have to implement the constructors that make sense for each derivation and then use the base keyword to direct that constructor to the appropriate base class or the this keyword to direct a constructor to another constructor in the same class.

If the compiler made assumptions about inheriting constructors, we wouldn't be able to properly determine how our objects were instantiated. In the most part, you should consider why you have so many constructors and consider reducing them to only one or two in the base class. The derived classes can then mask out some of them using constant values like null and only expose the necessary ones through their constructors.

Update

In C#4 you could specify default parameter values and use named parameters to make a single constructor support multiple argument configurations rather than having one constructor per configuration.


i think having to copy the signature of one constructor is too many. In my real situation i had one constructor, and i do not want to have to copy it. Then i have to change it in two places.
This is why upfront design is important. You shouldn't be coding with 100% conviction that it all has to change.
Invisible base class constructors are a result of choices made by designers of C# language (and quite possibly other CLR languages). That being sad it has nothing to do with upfront design of application but with limitations of language which renders design with many constructors in base class (often) impracticable.
In such context you're right although now "best practices" becomes "best available practices".
The most annoying one for me is custom exceptions - Sub New(), Sub New(Message As String), Sub New(Message As String, InnerEx as Exception), Sub New(Info As Serialization.SerializationInfo, Context As Serialization.StreamingContext)... Yawn `
K
Kon

387 constructors?? That's your main problem. How about this instead?

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

Bingo, people don't use params as much as they should
To be honest, I've never used them myself in any production code. I usually have List or some collection I pass. I long for the day where I get to leverage params. :)
Does your answer change if there was just one constructor?
I'd like to think SO would encourage people to provide a better solution to a higher level problem the questioner did not think to ask, rather than downvoting them for it.
That really only works if the parameters are the same type, and are used as a list to initialize the object. But if the constructors had even different signature (say one takes two ints, and another two string, and another and int and a string, and a fourth, an float and an enum. You'd be spending most of the constructor checking types and unbox -- doing things at run-time that really should be done by the compiler.
J
James Curran

Yes, you have to copy all 387 constructors. You can do some reuse by redirecting them:

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

but that's the best you can do.


佚名

Too bad we're kind of forced to tell the compiler the obvious:

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

I only need to do 3 constructors in 12 subclasses, so it's no big deal, but I'm not too fond of repeating that on every subclass, after being used to not having to write it for so long. I'm sure there's a valid reason for it, but I don't think I've ever encountered a problem that requires this kind of restriction.


Resharper makes this easy! (Alt+Insert, Constructors)
j
jcai

Don't forget that you can also redirect constructors to other constructors at the same level of inheritance:

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

E
Exocubic

Another simple solution could be to use a structure or simple data class that contains the parameters as properties; that way you can have all the default values and behaviors set up ahead of time, passing the "parameter class" in as the single constructor parameter:

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

This avoids the mess of multiple constructors (sometimes) and gives strong typing, default values, and other benefits not provided by a parameter array. It also makes it easy to carry forward since anything that inherits from Foo can still get to, or even add to, FooParams as needed. You still need to copy the constructor, but you always (most of the time) only (as a general rule) ever (at least, for now) need one constructor.

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

I really like the overloaded Initailize() and Class Factory Pattern approaches better, but sometimes you just need to have a smart constructor. Just a thought.


K
Keith

As Foo is a class can you not create virtual overloaded Initialise() methods? Then they would be available to sub-classes and still extensible?

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

This shouldn't have a higher performance cost unless you have lots of default property values and you hit it a lot.


Yes, that is a very good idea; although i would probably have initialize a static method that returns a class
P
Pavlo Neiman
public class BaseClass
{
    public BaseClass(params int[] parameters)
    {

    }   
}

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

    }
}

A
Aaron Murgatroyd

Personally I think this is a mistake on Microsofts part, they should have allowed the programmer to override visibility of Constructors, Methods and Properties in base classes, and then make it so that Constructors are always inherited.

This way we just simply override (with lower visibility - ie. Private) the constructors we DONT want instead of having to add all the constructors we DO want. Delphi does it this way, and I miss it.

Take for example if you want to override the System.IO.StreamWriter class, you need to add all 7 constructors to your new class, and if you like commenting you need to comment each one with the header XML. To make it worse, the metadata view dosnt put the XML comments as proper XML comments, so we have to go line by line and copy and paste them. What was Microsoft thinking here?

I have actually written a small utility where you can paste in the metadata code and it will convert it to XML comments using overidden visibility.


The issue isn't one of visibility. A child-class constructor cannot be used for building a derived-class object except when called from the derived class constructor. What is needed IMHO is a facility by which a child class can specify that for all parent-class constructors the compiler should auto-generate a child-class constructor with the same parameters, which calls the appropriate parent constructor and then a general-purpose child-class constructor template.
Apparently, this compiler option exists in C++ 11, but C# has fallen behind.
T
T.J. Crowder

Do i really have to copy all constructors from Foo into Bar and Bah? And then if i change a constructor signature in Foo, do i have to update it in Bar and Bah?

Yes, if you use constructors to create instances.

Is there no way to inherit constructors?

Nope.

Is there no way to encourage code reuse?

Well, I won't get into whether inheriting constructors would be a good or bad thing and whether it would encourage code reuse, since we don't have them and we're not going to get them. :-)

But here in 2014, with the current C#, you can get something very much like inherited constructors by using a generic create method instead. It can be a useful tool to have in your belt, but you wouldn't reach for it lightly. I reached for it recently when faced with needing to pass something into the constructor of a base type used in a couple of hundred derived classes (until recently, the base didn't need any arguments, and so the default constructor was fine — the derived classes didn't declare constructors at all, and got the automatically-supplied one).

It looks like this:

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

That says that you can call the generic create function using a type parameter which is any subtype of Foo that has a zero-arguments constructor.

Then, instead of doing Bar b new Bar(42), you'd do this:

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

There I've shown the create method being on Foo directly, but of course it could be in a factory class of some sort, if the information it's setting up can be set by that factory class.

Just for clarity: The name create isn't important, it could be makeThingy or whatever else you like.

Full Example

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

I was just about to post a template factory function solution... But then you beat me to it by 2.5 years
C
Chris Marasti-Georg

The problem is not that Bar and Bah have to copy 387 constructors, the problem is that Foo has 387 constructors. Foo clearly does too many things - refactor quick! Also, unless you have a really good reason to have values set in the constructor (which, if you provide a parameterless constructor, you probably don't), I'd recommend using property getting/setting.


Not addressing the real question. And doesn't help if class needs immutable state.
Hence the "unless you have a good reason". The point is, any class with 387 constructors does too much.
The question is unchanged if i have 2 constructors; how to solve the problem of having to copy-and-paste 2 lines.
C
C. Dragon 76

No, you don't need to copy all 387 constructors to Bar and Bah. Bar and Bah can have as many or as few constructors as you want independent of how many you define on Foo. For example, you could choose to have just one Bar constructor which constructs Foo with Foo's 212th constructor.

Yes, any constructors you change in Foo that Bar or Bah depend on will require you to modify Bar and Bah accordingly.

No, there is no way in .NET to inherit constructors. But you can achieve code reuse by calling a base class's constructor inside the subclass's constructor or by calling a virtual method you define (like Initialize()).


You shouldn't call virtual methods in constructors - a subclass will not have run its constructor at the time the virtual method is called.
Yes, that's probably good advice. You could have the virtual Initialize pattern be called by callers after construction though. The pattern would be Bar bar = new Bar(); bar.Initialize();
G
Greg D

You may be able to adapt a version of the C++ virtual constructor idiom. As far as I know, C# doesn't support covariant return types. I believe that's on many peoples' wish lists.


This sounds interesting, although i don't know what covariant means. :(
Basically, it means that you can override a function with the same parameters by changing its return type, so "string Function(int,int)" can be different from "int Function(int,int)". If you try this in C# now, it complains that the overload already exists.
t
tvanfosson

Too many constructors is a sign of a broken design. Better a class with few constructors and the ability to set properties. If you really need control over the properties, consider a factory in the same namespace and make the property setters internal. Let the factory decide how to instantiate the class and set its properties. The factory can have methods that take as many parameters as necessary to configure the object properly.


If you require your class to be immutable then you don't want to expose properties. Because then your class has state.
@Daniel, that's why I suggested the factory pattern using internal setters. In that way the setters are only exposed to the class that creates the object and the object can maintain (for all intents and purposes) its immutability.