6

我想知道在 C# 中链接构造函数时如何更改执行顺序。我见过的唯一方法要求首先在当前构造函数之外调用链式构造函数。

具体来说,举个例子:

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

理想情况下,我想通过这样做来减少代码重复(注意,我承认在这个人为的示例中,并没有保存太多代码,但我正在使用可以带来更多好处的代码。为了清楚起见,我使用这个示例):

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

这在 VB .Net 中是可能的,但 C# 不允许我在另一个构造函数的中间调用构造函数 - 仅在开始时使用 Foo() : this() 语法。

所以我的问题是,链接构造函数时如何控制构造函数调用的顺序,而不是使用冒号语法,只能先调用另一个构造函数?

4

4 回答 4

8

您不能在其他构造函数中调用构造函数。构造函数只能链接另一个构造函数,以便在它之前直接调用。

但是,为了解决您的特定问题,您可以创建一个可以接受两种类型参数的私有构造函数,并让您的两个原始构造函数都简单地链接这个,为缺少的参数传递 null 。

这比调用私有初始化方法具有优势,它可以很好地与readonly字段一起使用(即,如果myThingreadonly字段,它仍然可以工作):

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

如果您使用 C# 4.0,您还可以使用命名或可选参数:

http://msdn.microsoft.com/en-us/library/dd264739.aspx

于 2011-03-24T23:34:39.933 回答
7

我想知道在 C# 中链接构造函数时如何更改执行顺序。

你没有。C# 中没有这样的功能。

已经有一种以任意顺序调用任意代码的机制:创建一堆方法并按照您喜欢的顺序调用它们。

这是我关于这个主题的文章更详细。

http://blogs.msdn.com/b/ericlippert/archive/2010/01/28/calling-constructors-in-arbitrary-places.aspx

如果您对构造函数链接设计原则的主题感兴趣,那么您可能还想阅读

http://blogs.msdn.com/b/ericlippert/archive/2008/02/15/why-do-initializers-run-in-the-opposite-order-as-constructors-part-one.aspx

http://blogs.msdn.com/b/ericlippert/archive/2008/02/18/why-do-initializers-run-in-the-opposite-order-as-constructors-part-two.aspx

于 2011-03-24T23:25:15.443 回答
1

您在类中创建私有初始化方法,并让您的构造函数逻辑调用这些方法。

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
 }

}
于 2011-03-24T23:17:39.843 回答
0

你必须使用一种方法。最直接的翻译是:

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

至少只要您没有只读字段。我不得不承认,在任意位置调用其他构造函数的语法在那里有其优势。

于 2011-03-24T23:35:56.983 回答