构造函数链或使用命名/可选参数

6

我是一名非常新的程序员,了解到我们需要尽量减少代码冗余,这样在更新时就能尽可能减少更改并最小化错误。

因此,我有一个学生类,我想要重载构造函数,这样如果我进行回溯,就像这样。

 public class Student
{
    string name;
    int Id;

    public Student()
    {
    }
    public Student(string name)
    {
        this.name = name;
    }
    public Student(string name, int Id) :this(name)
    {
        this.Id = Id;

但是这显然不好,因为我们想要将所有的代码放在一个构造函数中,原因是什么呢?易读性吗?
    public Student() : this(null, 0)
    { }
    public Student(string name) : this(name, 0)
    { }
    public Student(string name, int Id) 
    {
        this.name = name;
        this.Id = Id;
    }

如果我使用正向链接,那么如果我们添加一个新的字段如字符串专业,会发生什么?如果我使用正向链接,则需要创建一个带有添加字段的新重载构造函数。但现在我必须更改所有其他构造函数以调用新的构造函数。如果我使用反向链接,我只需创建一个新的构造函数并调用先前的重载函数即可。

    public Student(string name, int Id, string major):this(name, Id)
    {
        this.major=major;
    } 

这似乎更符合面向对象编程的规范,但我教材中的所有示例都展示了正向链接,并没有说明为什么我不应该使用反向链接。

如果我使用命名/可选参数,那就更容易了。

    public Student(string name = null, int Id = 0, string major = null)
    {
        this.name = name;
        this.Id = Id;
        this.major = major;
    }

如果我需要另一个字段,只需要编辑唯一的构造函数。这似乎最符合面向对象编程原则,或者我错了吗?至少它可以消除最多的代码重复。我的任务是“按照面向对象编程原则实现学生类”。我知道所有这些都是有效的,但是有一种方式是最好/被接受的构造函数编码方式吗?我是否忽略了命名/可选参数的缺点?对于初学者来说,这么多编码方式非常令人困惑。


1
在 C# 中,可选参数之前广泛使用了链式构造函数。可选参数是在 C# 3.0 中添加的,因此许多信息仍然使用它们,因为这些信息比较旧。但是今天,如果在链中只传递默认值,那么链式构造函数已经失去了意义。 - Gusman
1
Gusman所说的话。此外,您可以一步一步地转发链:public Student() : this(null) {}而不是public Student() : this(null, 0) {}。(如果您有两个接受单个可空参数的构造函数,则可能会变得模糊不清)。这样,当您添加新字段时,您只需要更改链中的最后一个构造函数即可。 - Ted Hopp
4个回答

3

没有一种最佳方式,因为它们不同,每种方式都有优缺点。

C# 1.0版本就提供了独立构造函数和链式构造函数,在大多数情况下我们使用链式构造函数,但是如果两个构造函数需要完全处理不同的事情,则必须使用前者。

class Student
{
    public Student()
    {
        DoSomething();
    }

    public Student(string name)
    {
        this.Name = name;
        DoAnotherThing();
    }
}

与可选参数构造函数相比,上述两种构造函数要长得多,但说实话,它们更安全。考虑以下情况:
public class Student
{
    public Student(string firstName = null, string lastName = null)
    {
        this.FirstName = firstName;
        this.LastName = lastName;
    }

    public string FirstName { get; set; }
    public string LastName { get; set; }
}

//it's working fine
//var danny = new Student("danny", "chen");

//as the business grows a lot of students need a middle name
public Student(string firstName = null, string middleName = null, string lastName = null)
{
    this.FirstName = firstName;
    this.MiddleName = middleName;
    this.LastName = lastName;
}

//for a better sequence the programmer adds middleName between the existing two, bang!

它们之间的另一个区别是使用反射。可选参数不会为您生成更多的构造函数,如果您使用反射调用构造函数,则默认值不会应用。


1
我知道这些都是有效的,但是否有最佳/被接受的构造函数编码方式?
据我所知,没有最佳方式-你有很多选择。 与您合作的同事是达成共识的最佳人选。 C#正在成为一种非常丰富的语言,这意味着将有许多不同的方法来实现相同的事情。
命名/可选参数是否存在缺点,我是否遗漏了什么?
据我所知,没有缺点。 我个人认为这是最好的解决方案,但其他人可能会有不同的看法。
作为初学者,有这么多编码方式非常令人困惑。
欢迎来到编程世界。 数百万程序员正在忙于为世界增加复杂性!

1
我建议深入了解这个假设:
“但是显然这很糟糕,因为我们希望所有代码都在一个构造函数中,出于什么原因。”
流畅接口模式可以将这些东西分开,同时我无法强烈地争辩说这直接适用于构造函数。
customer
    .NameOfCustomer("Shiv")
    .Bornon("12/3/1075")
    .StaysAt("Mumbai");

说明性实现:

public class Customer
{
    public string FullName { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string Address { get; set; }

    public Customer NameOfCustomer(string name)
    {
        this.FullName = name;
        return this;
    }

    public Customer BornOn(DateTime dateOfBirth)
    {
        this.DateOfBirth = dateOfBirth;
        return this;
    }

    public Customer StaysAt(string address)
    {
        this.Address = address;
        return this;
    }
} 

示例源代码


0

这是一个部分回答。你的“向后链接”(backwards chaining)方式,使用一个构造函数设置一些参数,然后调用另一个构造函数完成其余部分,对于普通的函数来说非常合理/明智:一个函数完成部分工作,然后调用另一个函数完成其他工作,以此类推。

但对于公有构造函数,客户端代码可以调用任何一个,期望得到一个完全构造的对象,完全初始化。你的示例中的某些构造函数将成员留在未初始化状态(除非你想要类型的默认值)。

作为替代方案,你可以使用私有构造函数。


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