base()和this()构造函数的最佳实践

108

在我的构造函数括号后面(甚至是代码中的其他位置)调用:base():this()构造函数是在什么条件下使用的。这些调用是何时是良好实践,何时是强制性要求?


抱歉有些含糊,我知道这些调用在做什么。我只是不清楚何时或必须进行这些调用的情况。如果我不进行这些调用,那么会立即出现问题,或者会存在潜在的错误或代码质量差的情况。最近我遇到了这样的问题,因此我想澄清这些情况。 - explorer
5个回答

132

: base(...)

如果省略对基类构造函数的调用,它将自动调用默认的基类构造函数。

如果没有默认构造函数,则必须显式地调用基类构造函数。

即使有默认构造函数,您可能仍希望调用不同于默认构造函数的其他构造函数。在这种情况下,您仍然可以使用 base(foo, bar) 来调用不同于基类构造函数的构造函数。

当您想要调用基类默认构造函数时,省略 base() 不会被认为是一种不好的做法,虽然如果您喜欢明确,包含它也没有问题。这是一个品味问题。

: this(...)

这种语法允许您在同一个类中使用不同签名的一个构造函数来调用另一个构造函数。这并不是强制性的,但有时是有用的。

例如,在构造函数中重用公共代码时可以使用这种方式。例如,在C# 3.5或之前,您可能希望模拟构造函数上的可选参数:

Foo(int x, int y)
{
     this.x = x;
     this.y = y;
}

Foo(int x) : this(x, 10) {}  // y defaults to 10

使用C# 4.0,现在可以使用可选参数来减少对这种方法的需求。

重用构造函数中的代码的另一种方法是将其分解为静态函数,并从希望使用它的每个构造函数中调用它。


45

当存在继承且父类已提供您想要实现的功能时,请使用base

当您想要引用当前实体(或self)时,请使用this。在构造函数的头/签名中使用它,当您不想重复另一个构造函数中已定义的功能时。

基本上,在构造函数的头中使用basethis是为了使您的代码DRY,使其更易于维护且不冗长。

这里有一个完全没有意义的例子,但我认为它说明了如何使用这两个东西的想法。

class Person
{
    public Person(string name)
    {
        Debug.WriteLine("My name is " + name);
    }
}

class Employee : Person
{
    public Employee(string name, string job)
        : base(name)
    {
        Debug.WriteLine("I " + job + " for money.");
    }

    public Employee() : this("Jeff", "write code")
    {
        Debug.WriteLine("I like cake.");
    }
}

使用方法:

var foo = new Person("ANaimi");
// output:
//  My name is ANaimi

var bar = new Employee("ANaimi", "cook food");
// output:
//  My name is ANaimi
//  I cook food for money.

var baz = new Employee();
// output:
//  My name is Jeff
//  I write code for money.
//  I like cake.

38

首先,它们何时是必需的。

当一个类 Derived 从一个没有默认(无参数)构造函数的类 Base 派生时,Derived 必须显式地使用参数调用 base()

public class Base {
    public Base(int i) { }
}


public class Derived : Base {
    // public Derived() { } wouldn't work - what should be given for i?
    public Derived() : base(7) { }
    public Derived(int i) : base(i) { }
}

什么时候使用这种技术?每当你想要调用不同的构造函数时。

假设在我的之前示例中,你向Derived的构造函数添加了内容。

public class Derived : Base {
    // public Derived() { } wouldn't work - what should be given for i?
    public Derived() : base(7) {
        Console.WriteLine("The value is " + 7);
    }
    public Derived(int i) : base(i) {
        Console.WriteLine("The value is " + i);
    }
}

你注意到这里的重复了吗?调用this()构造函数会更简单。
public class Derived : Base {
    // public Derived() { } wouldn't work - what should be given for i?
    public Derived() : this(7) { }
    public Derived(int i) : base(i) {
        Console.WriteLine("The value is " + i);
    }
}

因此,在最后一个示例中,调用顺序将是:base(7),然后是Derived(7),然后是 Derived()。换句话说,this(7) 部分本身不会触发 base()(假设有这样的基方法)。这个理解正确吗? - RayLuo
1
正确;构造函数总是链接的,每个类都调用其基类的构造函数。使用this可以重用此类的其他构造函数(它本身将使用base,无论是隐式地没有参数还是显式地);使用base可以选择调用哪个基类构造函数。 - configurator

10

搜索"C#中的构造函数链接"。基本上,它看起来像这样:

MyClass():base()  //default constructor calling superclass constructor
{
}

MyClass(int arg):this()  //non-basic constructor calling base constructor
{
    //extra initialization
}

将构造函数分成基本部分和特定部分有助于消除代码重复。


谢谢。我想不起来的术语是“构造函数链”。 - JNappi

6

当您希望基类的构造函数作为您的构造函数的第一条指令自动调用时,可以使用:base()。而:this()则类似,但它调用同一类中的另一个构造函数。

在:base()和:this()中,您可以将常量值或基于构造函数参数的表达式作为参数传递。

当基类没有默认构造函数(不带参数的构造函数)时,调用基础构造函数是必需的。我不知道是否有必须使用:this()的情况。

public class ABaseClass
{
    public ABaseClass(string s) {}
}

public class Foo : AChildClass
{
    public AChildClass(string s) : base(s) {} //base mandatory
    public AChildClass() : base("default value") {}  //base mandatory
    public AChildClass(string s,int i) : base(s+i) {}  //base mandatory
}

public class AnotherBaseClass
{
    public ABaseClass(string s) {}
    public ABaseClass():this("default value") {} //call constructor above
}

public class Foo : AnotherChildClass
{
    public AnotherChildClass(string s) : base(s) {} //base optional

}

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