如何在 C# 中进行构造函数链接

发布于 2024-08-12 17:55:37 字数 394 浏览 5 评论 0原文

我知道这应该是一个超级简单的问题,但我已经为这个概念苦苦挣扎了一段时间。

我的问题是,如何在 C# 中链接构造函数?

我正在参加我的第一堂 OOP 课程,所以我只是在学习。我不明白构造函数链接是如何工作的,也不明白如何实现它,甚至不明白为什么它比只在没有链接的情况下创建构造函数更好。

我希望有一些带有解释的例子。

那么如何链接它们呢?
我知道两个是这样的:

public SomeClass this: {0}

public SomeClass
{
    someVariable = 0
} 

但是如果是三个、四个等等,你怎么做呢?

再说一遍,我知道这是一个初学者问题,但我很难理解这一点,而且我不知道为什么。

I know that this is supposedly a super simple question, but I've been struggling with the concept for some time now.

My question is, how do you chain constructors in C#?

I'm in my first OOP class, so I'm just learning. I don't understand how constructor chaining works or how to implement it or even why it's better than just doing constructors without chaining.

I would appreciate some examples with an explanation.

So how do how chain them?
I know with two it goes:

public SomeClass this: {0}

public SomeClass
{
    someVariable = 0
} 

But how do you do it with three, four and so on?

Again, I know this is a beginner question, but I'm struggling to understand this and I don't know why.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

冬天旳寂寞 2024-08-19 17:55:37

您可以使用标准语法(像方法一样使用 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) 
    {
    }
}

then:

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

另请注意:

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

对于“为什么?”:

  • code减少(总是一件好事)
  • 有必要调用非默认基构造函数,例如:

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

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

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

You use standard syntax (using this like a method) to pick the overload, inside the class:

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

then:

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

Note also:

  • you can chain to constructors on the base-type using base(...)
  • you can put extra code into each constructor
  • the default (if you don't specify anything) is base()

For "why?":

  • code reduction (always a good thing)
  • necessary to call a non-default base-constructor, for example:

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

Note that you can also use object initializers in a similar way, though (without needing to write anything):

SomeType x = new SomeType(), y = new SomeType { Key = "abc" },
         z = new SomeType { DoB = DateTime.Today };
生死何惧 2024-08-19 17:55:37

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

但是,如果您仍使用 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 年以来,开发人员一直在抱怨构造链并且无法使用默认的可选参数!现在它在开发领域已经花了很长时间,开发人员害怕使用它,因为它不向后兼容。

I just want to bring up a valid point to anyone searching for this. If you are going to work with .NET versions before 4.0 (VS2010), please be advised that you have to create constructor chains as shown above.

However, if you're staying in 4.0, I have good news. You can now have a single constructor with optional arguments! I'll simplify the Foo class example:

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

When you implement the constructor, you can use the optional arguments since defaults have been set.

I hope you enjoyed this lesson! I just can't believe that developers have been complaining about construct chaining and not being able to use default optional arguments since 2004/2005! Now it has taken SO long in the development world, that developers are afraid of using it because it won't be backwards compatible.

脱离于你 2024-08-19 17:55:37

最好用一个例子来说明这一点。想象一下,我们有一个 Person 类,

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

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

This is best illustrated with an example. Imaging we have a class Person

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

So here we have a constructor which sets some properties, and uses constructor chaining to allow you to create the object with just a name, or just a name and address. If you create an instance with just a name this will send a default value, string.Empty through to the name and address, which then sends a default value for Postcode through to the final constructor.

In doing so you're reducing the amount of code you've written. Only one constructor actually has code in it, you're not repeating yourself, so, for example, if you change Name from a property to an internal field you need only change one constructor - if you'd set that property in all three constructors that would be three places to change it.

回忆追雨的时光 2024-08-19 17:55:37

“构造函数链”的用法是什么?
您可以使用它从一个构造函数调用另一个构造函数。

如何实现“构造器链”?
在构造函数定义后使用“: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个或更多构造函数。如果没有“构造函数链”,则必须在类的所有构造函数中重复初始化代码。

它是如何工作的? (或者,“构造函数链”中的执行顺序是什么)?
在上面的例子中,方法“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;
    }
}

What is usage of "Constructor Chain"?
You use it for calling one constructor from another constructor.

How can implement "Constructor Chain"?
Use ": this (yourProperties)" keyword after definition of constructor. for example:

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

Why is it useful?
Important reason is reduce coding, and prevention of duplicate code. such as repeated code for initializing property
Suppose some property in class must be initialized with specific value (In our sample, requestDate). And class have 2 or more constructor. Without "Constructor Chain", you must repeat initializaion code in all constractors of class.

How it work? (Or, What is execution sequence in "Constructor Chain")?
in above example, method "a" will be executed first, and then instruction sequence will return to method "b".
In other word, above code is equal with below:

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;
    }
}
小梨窩很甜 2024-08-19 17:55:37

我有一个日记班,所以我不会一次又一次地写设置值

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

I have a diary class and so i am not writing setting the values again and again

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;
}
我只土不豪 2024-08-19 17:55:37

所有这些答案都很好,但我想在具有更复杂初始化的构造函数上添加注释。

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

尽管这个例子也可以在没有这个静态函数的情况下解决,但静态函数允许更复杂的逻辑,甚至可以从其他地方调用方法。

All those answers are good, but I'd like to add a note on constructors with a little more complex initializations.

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 
quot;{a} + {b} = {c}";
    }
}

Allthogh this example might as well be solved without this static function, the static function allows for more complex logic, or even calling methods from somewhere else.

以歌曲疗慰 2024-08-19 17:55:37

你是问这个吗?

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

}

Are you asking about this?

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

}
月棠 2024-08-19 17:55:37

我希望下面的示例能够对构造函数链有所启发。
例如,我的用例是,您希望用户将目录传递给您的
构造函数,用户不知道要传递哪个目录并决定让
您指定默认目录。您指定一个您认为的默认目录
会起作用的。

顺便说一句,我在这个示例中使用了 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;
    }
}

I hope following example shed some light on constructor chaining.
my use case here for example, you are expecting user to pass a directory to your
constructor, user doesn't know what directory to pass and decides to let
you assign default directory. you step up and assign a default directory that you think
will work.

BTW, I used LINQPad for this example in case you are wondering what *.Dump() is.
cheers

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;
    }
}
清泪尽 2024-08-19 17:55:37

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

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

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

There's another important point in constructor chaining: order.
Why?
Let's say that you have an object being constructed at runtime by a framework that expects it's default constructor. If you want to be able to pass in values while still having the ability to pass in constructor argments when you want, this is extremely useful.

I could for instance have a backing variable that gets set to a default value by my default constructor but has the ability to be overwritten.

public class MyClass
{
  private IDependency _myDependency;
  MyClass(){ _myDependency = new DefaultDependency(); }
  MYClass(IMyDependency dependency) : this() {
    _myDependency = dependency; //now our dependency object replaces the defaultDependency
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文