可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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?
回答1:
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.
回答2:
387 constructors?? That's your main problem. How about this instead?
public Foo(params int[] list) {...}
回答3:
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.
回答4:
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.
回答5:
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) { ... }
^^^^^
回答6:
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.
回答7:
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.
回答8:
public class BaseClass
{
public BaseClass(params int[] parameters)
{
}
}
public class ChildClass : BaseClass
{
public ChildClass(params int[] parameters)
: base(parameters)
{
}
}
回答9:
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.
回答10:
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);
}
}
}
回答11:
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.
回答12:
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()).
回答13:
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.
回答14:
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.