在C#中从一个构造函数中调用另一个构造函数

66

我需要在一个构造函数中调用另一个构造函数。我该怎么做?

基本上

class foo {
    public foo (int x, int y)
    {
    }

    public foo (string s)
    {
        // ... do something

        // Call another constructor
        this (x, y); // Doesn't work
        foo (x, y); // neither
    }
}

像这样的构造函数怎么样:public foo(int x, int y, string s)? - Jon Raynor
6个回答

93

你不能这样做。

你需要找到一种方法来链接构造函数,例如:

public foo (int x, int y) { }
public foo (string s) : this(XFromString(s), YFromString(s)) { ... }

或将您的构建代码移动到一个公共的设置方法中,就像这样:

public foo (int x, int y) { Setup(x, y); }
public foo (string s)
{
   // do stuff
   int x = XFromString(s);
   int y = YFromString(s);
   Setup(x, y);
}

public void Setup(int x, int y) { ... }

23
请注意,通常情况下,设置方法无法写入readonly变量,但构造函数可以将readonly变量作为ref参数传递给设置方法,即使它们是只读变量,设置方法也可以写入其ref参数。 - supercat
8
Setup 方法在大多数情况下应该是私有的。 - Chris Halcrow
观察第一种解决方案,XFromStringYFromString必须是静态方法。这是我喜欢的方式! - AxelWass

43

this(x, y) 是正确的,但它必须在构造函数体的开始位置:

public Foo(int x, int y)
{
    ...
}

public Foo(string s) : this(5, 10)
{
}

请注意:

  • 你只能链接到一个构造函数,可以是thisbase——当然,该构造函数也可以链接到另一个构造函数。
  • 构造函数体在链接的构造函数调用之后执行。没有办法先执行构造函数体。
  • 您不能在其他构造函数的参数中使用this,包括调用实例方法,但您可以调用静态方法。
  • 任何实例变量初始化器都会在链接调用之前执行。

我在我的构造函数链接文章中有更多信息。


当你有这样的代码时,这尤其令人恼火:class A{ public A(IEnumerable<int> items) {...} public A(MyClass c) : this(c.Items) { ... } }。第二个构造函数中的任何空值检查都只会在访问c之后执行。如果c为空,则第二个构造函数将抛出NRE,而不是首选的“ArgumentNullException”。如果您想保持字段只读,则除了在两个构造函数中实现相同的逻辑之外,没有其他选择。或者,是否有更好的方法来做到这一点? - Daniel Hilgarth
2
@DanielHilgarth: 是的,有的。有一个通用扩展方法,可以检查是否为null,但如果不是,则返回原始值。然后你就可以这样做:this(c.ThrowIfNull("c").Items) - Jon Skeet
谢谢,那也是我能想到的唯一方法。只是想编辑我的评论...不确定这是否是一个好方法,因为我迄今还没有看到过它在任何地方使用过... - Daniel Hilgarth
请翻译底部引用的文章:https://web.archive.org/web/20181103085311/http://www.yoda.arachsys.com/csharp/constructors.html - Henry Malinowski
@HenryMalinowski:谢谢 - 已经更新了链接到非Yoda位置。 - Jon Skeet
显示剩余4条评论

7
要显式调用基类和当前类的构造函数,您需要使用以下语法(请注意,在C#中,您不能像在C ++中那样使用它来初始化字段):
class foo
{
    public foo (int x, int y)
    {
    }

    public foo (string s) : this(5, 6)
    {
        // ... do something
    }
}

//编辑:注意到你在样例中使用了x,y。当然,在调用构造函数时给出这样的值不能依赖于其他构造函数的参数,它们必须以其他方式解决(但它们不需要像上面编辑后的代码样例中那样是常量)。如果x和y是由s计算得出的,可以这样做:

public foo (string s) : this(GetX(s), GetY(s))

2
小注:public foo(string s) : this(x, y) 不起作用。public foo(string s, int x, int y) : this(x, y) 将编译通过。 - Łukasz Wiatrak
或许值得注意的是,GetX()GetY()必须是静态方法(显而易见的是,在构造函数完成之前,没有实例可以调用实例方法)。 - Bob Sammers

3
这是不支持的 - 请参见C#中的构造函数
但是,您可以实现一个常用(私有)方法,然后从不同的构造函数中调用它...

1

我自己也遇到过这个问题...最终我不得不将我需要的任何逻辑从另一个构造函数中提取出来,放入一个private void方法中,并在两个地方都调用它。

class foo
{
  private void Initialize(int x, int y)
  {
    //... do stuff
  }

  public foo(int x, int y)
  {
    Initialize(x, y);
  }

  public foo(string s_
  {
    // ... do stuff

    Initialize(x, y)
    // ... more stuff
  }
}

0

在 MSDN 的 MethodBase.Invoke description 中有一个注释:

如果使用此方法重载来调用实例构造函数,则为 obj 提供的对象将被重新初始化;也就是说,将执行所有实例初始值设定项。返回值为 null。如果调用类构造函数,则该类将被重新初始化;也就是说,将执行所有类初始值设定项。返回值为 null。

也就是说,您可以通过反射获取构造函数的方法,并在新构造函数体中通过 Invoke 调用它。但我没有试过这个方法。当然,这种方法也有很多缺点。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接