哪一个是构造函数的最佳实践



我查看了微软的文档,最佳实践应该是第二个。但我仍然对此感到困惑。我在程序中使用了这两个构造函数,没有任何问题。我想知道到底有什么区别?

public class Person
{
// fields
private string _firstName;
private string _lastName;
// data accessor
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
// constructor
public Person(string fn, string ln)
{
_firstName = fn;
_lastName = ln;
}
}
public class Person
{
// fields
private string _firstName;
private string _lastName;
// data accessor
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
// constructor
public Person(string fn, string ln)
{
FirstName = fn;
LastName = ln;
}
}

由于类是可变的,并且名字和姓氏都可以为null,因此在实例化时设置字段实际上不是强制性的。所以你可以省去一些打字,只需这样做:

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

并以这种方式实例化:

var instance = new Person { FirstName = "John", LastName = "Doe" };

就我个人而言,我更喜欢不可变的类,所以我更喜欢这样的类:

public class Person
{
private readonly string _firstName;
private readonly string _lastName;
public string FirstName => _firstName;
public string LastName => _lastName;
public Person (string firstName, string lastName)
{
if (firstName == null) throw new ArgumentNullException("FirstName");
if (lastName == null) throw new ArgumentNullException("LastName");
_firstName = firstName;
_lastName = lastName;
}
}

使用不可变版本,您可以确信,从实例化的那一刻起,名字和姓氏都是有效的。因此实例总是有效的。对于您的类,无论您在构造函数中如何设置,都不能。

我怀疑微软正在推广第二个例子中演示的技术,以鼓励在定义的属性中使用setter,以防他们在为成员字段分配值之外执行任何操作(如Ken White上文所述(。目前情况并非如此,所以你的困惑是可以理解的。

然而,随着代码的增长,属性可能会演变并呈现以下形状(假设Person现在正在实现INotifyPropertyChanged接口,并使用服务将更改传输到对等网络服务,就像游戏一样(:

private readonly ISomePeerToPeerService _peerService;
public Person(string fn, string ln, ISomePeerToPeerServicepeerService peerService)
{
_firstName = fn;
_lastName = ln;
_peerService = peerService;
}

public string LastName
{
get { return _lastName; }
set 
{ 
if (Equals(_lastName, value)
return;
_lastName = value; 
OnPropertyChanged()
}
private void OnPropertyChanged([CallerMemberName] string memberName = null)
{
_peerService.Update(this); // Update peers

// notify stuff
}
}

更重要的是要确保所有代码(包括Person的内部(都能调用这些属性,而不是直接修改字段。否则,自定义逻辑将不会被调用。

另一个例子是如果一个人结婚了:

public BankAccount SharedBankAccount { get { ... }  set { ... } }
public void MarryTheGroom(Person groom)
{
LastName = groom.LastName;
SharedBankAccount = groom.PersonalAccount; // he he
}

结论

这与意外公开字段,然后客户端代码直接修改字段非常相似,而不是调用TransferFundsBeteenBankAccounts(x, y)之类的方法

最新更新