C# constructor chaining - changing the order of execution
I want to know how to change the order of execution when chaining constructors in C#. The only methods I have seen require the chained constructor to be called first, outside of the current constructor.
Specifically, take the following example:
public class Foo {
private static Dictionary<string, Thing> ThingCache = new Dictionary<string, Thing>();
private Thing myThing;
public Foo(string name) {
doSomeStuff();
if (ThingCache.ContainsKey(name)) {
myThing = ThingCache[name];
} else {
myThing = ExternalStaticFactory.GetThing(name);
ThingCache.Add(name, myThing);
}
doSomeOtherStuff();
}
public Foo(Thing tmpThing) {
doSomeStuff();
myThing = tmpThing;
doSomeOtherStuff();
}
}
Ideally, I'd like to reduce code repetition by doing this (note, I admit that in this contrived example, not much code is saved, but I am working with code that would benefit much more. I use this example for clarity):
public class Foo {
private static Dictionary<string, Thing> ThingCache = new Dictionary<string, Thing>();
private Thing myThing;
public Foo(string name) {
if (ThingCache.ContainsKey(name)) {
this(ThingCache[name]);
} else {
this(ExternalStaticFactory.GetThing(name));
ThingCache.Add(name, myThing);
}
}
public Foo(Thing tmpThing) {
doSomeStuff();
myThing = tmpThing;
doSomeOtherStuff();
}
}
This is possible in VB .Net, but C# doesn't let me call a constructor in the middle of another constructor - only at the beginning using the Foo() : this() syntax.
So my question is, how does one control the order of constructor calling when chaining constructors, rather than using the colon syntax, which ca开发者_JS百科n only call the other constructor first?
You can't call constructors inside other constructors. A constructor can only chain another constructor to be called directly before it.
However, to solve your particular issue, you can make a private constructor that can accept both a types of argument, and have your two original constructors both simply chain this one, passing null for the missing argument.
This has the advantage over calling private initialisation methods that it plays nicely with readonly
fields (i.e. it still works if myThing
is a readonly
field):
public class Foo
{
private static Dictionary<string, Thing> ThingCache =
new Dictionary<string, Thing>();
private Thing myThing;
public Foo(string name)
: this(null, name)
{
}
public Foo(Thing tmpThing)
: this(tmpThing, null)
{
}
private Foo(Thing tmpThing, string name)
{
if (tmpThing == null && name == null)
{
throw new System.ArgumentException(
"Either tmpThing or name must be non-null.");
}
doSomeStuff();
if (tmpThing != null)
{
myThing = tmpThing;
}
else
{
if (ThingCache.ContainsKey(name))
{
myThing = ThingCache[name];
}
else
{
myThing = ExternalStaticFactory.GetThing(name);
ThingCache.Add(name, myThing);
}
}
doSomeOtherStuff();
}
}
You could also use named or optional arguments if you are using C# 4.0:
http://msdn.microsoft.com/en-us/library/dd264739.aspx
I want to know how to change the order of execution when chaining constructors in C#.
You don't. There is no such feature in C#.
There already is a mechanism for calling arbitrary code in an arbitrary order: make a bunch of methods and call them in the order you like.
Here's my article on the subject going into more detail.
http://blogs.msdn.com/b/ericlippert/archive/2010/01/28/calling-constructors-in-arbitrary-places.aspx
If the topic of constructor chaining design principles interests you then you might also want to read
http://blogs.msdn.com/b/ericlippert/archive/2008/02/15/why-do-initializers-run-in-the-opposite-order-as-constructors-part-one.aspx
and
http://blogs.msdn.com/b/ericlippert/archive/2008/02/18/why-do-initializers-run-in-the-opposite-order-as-constructors-part-two.aspx
You create private initialization methods inside your class and have your constructor logic call those methods.
class Foo
{
public Foo(string name)
{
InitializeBefore();
if (ThingCache.ContainsKey(name))
{
myThing = ThingCache[name];
} else
{
myThing = ExternalStaticFactory.GetThing(name);
ThingCache.Add(name, myThing);
}
InitializeAfter();
}
public Foo(Thing tmpThing)
{
InitializeBefore();
myThing = tmpThing;
InitializeAfter();
}
private void InitializeBefore()
{
doSomeStuff();
// and any other calls you want before
}
private void InitializeAfter()
{
doSomeOtherStuff();
// and any other calls you want at the end of the constructor
}
}
You have to use a method. The most straightforward translation would be:
public class Foo {
private static Dictionary<string, Thing> ThingCache = new Dictionary<string, Thing>();
private Thing myThing;
public Foo(string name) {
if (ThingCache.ContainsKey(name)) {
Init(ThingCache[name]);
} else {
Init(ExternalStaticFactory.GetThing(name));
ThingCache.Add(name, myThing);
}
}
public Foo(Thing tmpThing) {
Init(tmpThing);
}
private void Init(Thing tmpThing) {
doSomeStuff();
myThing = tmpThing;
doSomeOtherStuff();
}
}
At least as long as you have no readonly fields. I have to admit, that a syntax to call other constructors at arbitrary locations has its advantages there.
精彩评论