C#构造函数链接? (怎么做?)

时间:2009-11-29 07:56:34

标签: c# constructor constructor-chaining

我知道这应该是一个非常简单的问题,但我一直在努力解决这个概念。我的问题是,你如何在c#中链接构造函数?我是第一个OOP课程,所以我只是在学习。我不明白构造函数链如何工作或如何实现它,甚至为什么它不仅仅是没有链接的构造函数。

我会很感激一些解释的例子。

那么如何将它们联系起来呢?我知道其中有两个:

public SomeClass this: {0}

public SomeClass
{
    someVariable = 0
} 

但你怎么用三,四等呢?

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

9 个答案:

答案 0 :(得分:308)

您使用标准语法(使用this方法)来选择重载,内部类:

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) {
    }
}

然后:

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

另请注意:

  • 您可以使用base(...)
  • 链接到基类型的构造函数
  • 您可以在每个构造函数中添加额外的代码
  • 默认值(如果您未指定任何内容)为base()

对于“为什么?”:

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

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

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

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

答案 1 :(得分:56)

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

然而,如果你留在4.0,我有好消息。您现在可以拥有一个带有可选参数的构造函数!我将简化Foo类示例:

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以来无法使用默认的可选参数!现在它已经在开发领域花了很长时间,开发人员害怕使用它,因为它不会向后兼容。

答案 2 :(得分:28)

最好用一个例子来说明。成像我们有一个人类

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到名称和地址,然后将Postcode的默认值发送到最终的构造函数。

这样做可以减少您编写的代码量。只有一个构造函数实际上有代码,你不是自己重复,所以,例如,如果你将Name从一个属性更改为一个内部字段,你只需要改变一个构造函数 - 如果你在所有三个构造函数中设置了该属性这将是改变它的三个地方。

答案 3 :(得分:10)

我有日记课,所以我不是一次又一次地设置值

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

答案 4 :(得分:7)

" Constructor Chain" 的用法是什么? 您可以使用它从另一个构造函数中调用一个构造函数。

如何实施"构造链"?
使用":this(yourProperties)"构造函数定义后的关键字。例如:

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

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

如何运作? (或者,"构造函数链&#34中的执行顺序是什么?)?
在上面的例子中,方法" a"将首先执行,然后指令序列将返回方法" b"。 换句话说,上面的代码与下面的代码相同:

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

答案 5 :(得分:6)

你在问这个吗?

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

}

答案 6 :(得分:2)

我希望以下示例阐明构造函数链接 以我的用例为例,您希望用户将目录传递给您 构造函数,用户不知道要传递什么目录并决定让它 您指定默认目录。你升级并分配一个你认为的默认目录 会工作的。

顺便说一下,我在这个例子中使用了LINQPad,以防你想知道* .Dump()是什么。
欢呼

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

答案 7 :(得分:1)

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

例如,我可以使用一个支持变量,该变量由我的默认构造函数设置为默认值,但是能够被覆盖。

public class MyClass
{
  private IDependency _myDependency;
  MyClass(){ _myDependency = new DefaultDependency(); }
  MYClass(IMyDependency dependency) : this() {
    _myDependency = dependency; //now our dependency object replaces the defaultDependency
  }
}

答案 8 :(得分:1)

所有这些答案都是好的,但是我想在构造函数上添加一些注释,这些构造函数的初始化要稍微复杂一些。

class SomeClass {
    private int StringLength;
    SomeClass(string x) {
         // this is the logic that shall be executed for all constructors.
         // you dont want to duplicate it.
         StringLength = x.Length;
    }
    SomeClass(int a, int b): this(TransformToString(a, b)) {
    }
    private static string TransformToString(int a, int b) {
         var c = a + b;
         return $"{a} + {b} = {c}";
    }
}

如果没有此静态函数,此示例也可以解决,该静态函数允许使用更复杂的逻辑,甚至可以从其他位置调用方法。