C#构造函数链接?

C# constructor chaining? (How to do it?)

我知道这应该是一个非常简单的问题,但我已经在这个概念上挣扎了一段时间了。我的问题是,如何在C中链接构造函数?我在上第一节OOP课,所以我只是在学习。我不知道构造函数链接是如何工作的,也不知道如何实现它,甚至不知道为什么它比不链接就做构造函数更好。

我会欣赏一些例子和解释。

那么,如何将它们链接起来呢?我知道有两个原因:

1
2
3
4
5
6
public SomeClass this: {0}

public SomeClass
{
    someVariable = 0
}

但是你怎么用三个,四个等等?

同样,我知道这是一个初学者的问题,但我很难理解这一点,也不知道为什么。


使用标准语法(像方法一样使用this)在类内部拾取重载:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Foo {
    private int id;
    private string name;
    public Foo() : this(0,"") {
    }
    public Foo(int id, string name) {
        this.id = id;
        this.name = name;
    }
    public Foo(int id) : this(id,"") {
    }
    public Foo(string name) : this(0, name) {
    }
}

然后:

1
Foo a = new Foo(), b = new Foo(456,"def"), c = new Foo(123), d = new Foo("abc");

注意事项:

  • 可以使用base(...)链接到基类型上的构造函数。
  • 您可以将额外的代码放入每个构造函数中
  • 默认值(如果您没有指定任何内容)是base()

"为什么?":

  • 代码缩减(总是好事)
  • 需要调用非默认的基构造函数,例如:

    1
    SomeBaseType(int id) : base(id) {...}

注意,也可以用类似的方式使用对象初始值设定项,尽管(不需要编写任何内容):

1
2
SomeType x = new SomeType(), y = new SomeType { Key ="abc" },
         z = new SomeType { DoB = DateTime.Today };


我只想向任何寻找这个的人提出一个有效的观点。如果您要使用4.0之前的.NET版本(VS2010),请注意您必须创建如上所示的构造函数链。

但是,如果你住在4.0,我有个好消息。现在您可以有一个带有可选参数的构造函数!我将简化foo类示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Foo {
  private int id;
  private string name;

  public Foo(int id = 0, string name ="") {
    this.id = id;
    this.name = name;
  }
}

class Main() {
  // Foo Int:
  Foo myFooOne = new Foo(12);
  // Foo String:
  Foo myFooTwo = new Foo(name:"Timothy");
  // Foo Both:
  Foo myFooThree = new Foo(13, name:"Monkey");
}

实现构造函数时,可以使用可选参数,因为已经设置了默认值。

希望你喜欢这节课!我简直不敢相信开发人员自2004/2005年以来就一直抱怨构造链接和不能使用默认的可选参数!现在,在开发世界中,开发人员已经花了很长的时间,害怕使用它,因为它不能向后兼容。


这最好用一个例子来说明。想象一下我们有一个班上的人

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public Person(string name) : this(name, string.Empty)
{
}

public Person(string name, string address) : this(name, address, string.Empty)
{
}

public Person(string name, string address, string postcode)
{
    this.Name = name;
    this.Address = address;
    this.Postcode = postcode;
}

所以这里我们有一个构造函数,它设置了一些属性,并使用构造函数链接来允许您只使用一个名称或名称和地址创建对象。如果只创建一个名为的实例,它将向名称和地址发送一个默认值string.empty,然后将邮政编码的默认值发送给最终的构造函数。

这样做是为了减少编写的代码量。实际上只有一个构造函数包含代码,您不会重复自己,因此,例如,如果您将名称从属性更改为内部字段,则只需更改一个构造函数-如果在所有三个构造函数中都设置了该属性,而这三个构造函数将是更改它的三个位置。


我有一个日记课,所以我不会一次又一次地写设定值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public Diary() {
    this.Like = defaultLike;
    this.Dislike = defaultDislike;
}

public Diary(string title, string diary): this()
{
    this.Title = title;
    this.DiaryText = diary;
}

public Diary(string title, string diary, string category): this(title, diary) {
    this.Category = category;
}

public Diary(int id, string title, string diary, string category)
    : this(title, diary, category)
{
    this.DiaryID = id;
}

"构造器链"的用法是什么?< BR>您可以使用它从另一个构造函数调用一个构造函数。

如何实现"构造链"?< BR>在构造函数定义之后使用":this(yourproperties)"关键字。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Class MyBillClass
{
    private DateTime requestDate;
    private int requestCount;

    public MyBillClass()
    {
        /// ===== we naming"a" constructor ===== ///
        requestDate = DateTime.Now;
    }
    public MyBillClass(int inputCount) : this()
    {
        /// ===== we naming"b" constructor ===== ///
        /// ===== This method is"Chained Method" ===== ///
        this.requestCount= inputCount;
    }
}

为什么它有用?< BR>重要原因是减少编码,防止重复编码。例如用于初始化属性的重复代码假设类中的某些属性必须用特定的值初始化(在我们的示例中为requestDate)。类有2个或更多的构造函数。如果没有"构造函数链",则必须在类的所有构造函数中重复初始化代码。

它是如何工作的?(或者,"构造函数链"中的执行顺序是什么)?< BR>在上面的例子中,首先执行方法"A",然后指令序列返回到方法"B"。换句话说,上面的代码等于下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Class MyBillClass
{
    private DateTime requestDate;
    private int requestCount;

    public MyBillClass()
    {
        /// ===== we naming"a" constructor ===== ///
        requestDate = DateTime.Now;
    }
    public MyBillClass(int inputCount) : this()
    {
        /// ===== we naming"b" constructor ===== ///
        // ===== This method is"Chained Method" ===== ///

        /// *** --- > Compiler execute"MyBillClass()" first, And then continue instruction sequence from here
        this.requestCount= inputCount;
    }
}

你在问这个吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  public class VariantDate {
    public int day;
    public int month;
    public int year;

    public VariantDate(int day) : this(day, 1) {}

    public VariantDate(int day, int month) : this(day, month,1900){}

    public VariantDate(int day, int month, int year){
    this.day=day;
    this.month=month;
    this.year=year;
    }

}


我希望下面的示例能够对构造函数链接有所帮助。
例如,我在这里的用例,您希望用户将目录传递给构造器,用户不知道要传递什么目录,决定让指定默认目录。您可以升级并分配一个您认为默认的目录有效。

顺便说一句,我在这个例子中使用了linqpad,以防您想知道什么是*.dump()。
干杯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void Main()
{

    CtorChaining ctorNoparam = new CtorChaining();
    ctorNoparam.Dump();
    //Result --> BaseDir C:\Program Files (x86)\Default\

    CtorChaining ctorOneparam = new CtorChaining("c:\\customDir");
    ctorOneparam.Dump();    
    //Result --> BaseDir c:\customDir
}

public class CtorChaining
{
    public string BaseDir;
    public static string DefaultDir = @"C:\Program Files (x86)\Default";


    public CtorChaining(): this(null) {}

    public CtorChaining(string baseDir): this(baseDir, DefaultDir){}

    public CtorChaining(string baseDir, string defaultDir)
    {
        //if baseDir == null, this.BaseDir = @"C:\Program Files (x86)\Default"
        this.BaseDir = baseDir ?? defaultDir;
    }
}

在构造函数链接中还有一个重要的点:顺序。为什么?假设有一个对象在运行时由一个框架构造,该框架期望它是默认的构造函数。如果您希望能够在传递值的同时仍然能够在需要时传递构造函数参数,那么这非常有用。

例如,我可以有一个后备变量,它由我的默认构造函数设置为默认值,但可以被覆盖。

1
2
3
4
5
6
7
8
public class MyClass
{
  private IDependency _myDependency;
  MyClass(){ _myDependency = new DefaultDependency(); }
  MYClass(IMyDependency dependency) : this() {
    _myDependency = dependency; //now our dependency object replaces the defaultDependency
  }
}