C# 在不可变中抛出异常 类
C# Throwing Exceptions in Immutable Classes
我正在阅读 here 关于在 C# 中创建不可变 classes 的内容。有人建议我以这种方式制作我的 classes 以实现真正的不变性:
private readonly int id;
public Person(int id) {
this.id = id;
}
public int Id {
get { return id; }
}
我明白了,但是如果我想做一些错误检查,我该如何抛出异常呢?鉴于以下class,我只能想这样做:
private readonly int id;
private readonly string firstName;
private readonly string lastName;
public Person(int id,string firstName, string lastName)
{
this.id=id = this.checkInt(id, "ID");
this.firstName = this.checkString(firstName, "First Name");
this.lastName = this.checkString(lastName,"Last Name");
}
private string checkString(string parameter,string name){
if(String.IsNullOrWhiteSpace(parameter)){
throw new ArgumentNullException("Must Include " + name);
}
return parameter;
}
private int checkInt(int parameter, string name)
{
if(parameter < 0){
throw new ArgumentOutOfRangeException("Must Include " + name);
}
return parameter;
}
这是正确的做法吗?如果不是,我将如何在不可变 classes 中抛出异常?
如果有必要将字段标记为只读 - 除了在 ctor 内部进行错误检查之外别无选择。只读字段只能在构造函数或初始化块中修改。
简单地做这样的事情:
public Person(int id) {
if(id > 1200) throw new Exception(); // or put it in separate method
this.Id = id;
}
您甚至可以更进一步,使用通用方法,例如:
private void CheckValue<T>(T parameter, string name)
{
if(T is int) if((parameter as int) < 0) throw new ArgumentOutOfRangeException(name));
if(T is string) if(String.IsEmptyOrWhiteSpace(parameter as string)) throw new ArgumentNullException(name));
// ... if (T is) ... other types here
}
}
在构造函数中:
public Person(int id, string name) {
CheckValue(id, "id");
CheckValue(name, "name");
this.Id = id;
}
我会内联这两个私有方法:
private readonly int id;
private readonly string firstName;
private readonly string lastName;
public Person(int id, string firstName, string lastName)
{
if(String.IsNullOrWhiteSpace(firstName))
throw new ArgumentNullException("firstName");
if(String.IsNullOrWhiteSpace(lastName))
throw new ArgumentNullException("lastName");
if(id < 0)
throw new ArgumentOutOfRangeException("id");
this.id=id;
this.firstName = firstName ;
this.lastName = lastName ;
}
如果你使用私有setter,属性可以通过反射设置,所以它不是不可变的。在此示例中,这对您来说可能是问题,也可能不是问题。就我个人而言,我更喜欢你的方法,因为它绝对不变,其他编码人员可以轻松推断出意图。
无论如何,你因为你的数据是不可变的,你的错误也是不可变的,所以永远不会改变。您遵循的模式是正确的。
您检查构造函数中的错误是正确的,因为您不想创建有错误的不可变对象。
[我本想把它写成评论,而不是答案,但我 ^H^H 的声誉差了 1 分。我现在就把它留在这里。]
C# 6 支持不可变的 getter-only auto 属性,因此如果您使用的是 Visual Studio 2015,您可以像这样进一步重构:
public int Id { get; }
public Person(int id, string firstName, string lastName)
{
if (id < 0)
throw new ArgumentOutOfRangeException("id");
Id=id;
}
我已将示例限制为 ID
属性。构造函数验证该值并将其分配给 属性(如果有效),并且 属性 是完全不可变的,而且您不必显式声明支持字段。 Resharper website.
上的重构演示很好地记录了这一点
我正在阅读 here 关于在 C# 中创建不可变 classes 的内容。有人建议我以这种方式制作我的 classes 以实现真正的不变性:
private readonly int id;
public Person(int id) {
this.id = id;
}
public int Id {
get { return id; }
}
我明白了,但是如果我想做一些错误检查,我该如何抛出异常呢?鉴于以下class,我只能想这样做:
private readonly int id;
private readonly string firstName;
private readonly string lastName;
public Person(int id,string firstName, string lastName)
{
this.id=id = this.checkInt(id, "ID");
this.firstName = this.checkString(firstName, "First Name");
this.lastName = this.checkString(lastName,"Last Name");
}
private string checkString(string parameter,string name){
if(String.IsNullOrWhiteSpace(parameter)){
throw new ArgumentNullException("Must Include " + name);
}
return parameter;
}
private int checkInt(int parameter, string name)
{
if(parameter < 0){
throw new ArgumentOutOfRangeException("Must Include " + name);
}
return parameter;
}
这是正确的做法吗?如果不是,我将如何在不可变 classes 中抛出异常?
如果有必要将字段标记为只读 - 除了在 ctor 内部进行错误检查之外别无选择。只读字段只能在构造函数或初始化块中修改。
简单地做这样的事情:
public Person(int id) {
if(id > 1200) throw new Exception(); // or put it in separate method
this.Id = id;
}
您甚至可以更进一步,使用通用方法,例如:
private void CheckValue<T>(T parameter, string name)
{
if(T is int) if((parameter as int) < 0) throw new ArgumentOutOfRangeException(name));
if(T is string) if(String.IsEmptyOrWhiteSpace(parameter as string)) throw new ArgumentNullException(name));
// ... if (T is) ... other types here
}
}
在构造函数中:
public Person(int id, string name) {
CheckValue(id, "id");
CheckValue(name, "name");
this.Id = id;
}
我会内联这两个私有方法:
private readonly int id;
private readonly string firstName;
private readonly string lastName;
public Person(int id, string firstName, string lastName)
{
if(String.IsNullOrWhiteSpace(firstName))
throw new ArgumentNullException("firstName");
if(String.IsNullOrWhiteSpace(lastName))
throw new ArgumentNullException("lastName");
if(id < 0)
throw new ArgumentOutOfRangeException("id");
this.id=id;
this.firstName = firstName ;
this.lastName = lastName ;
}
如果你使用私有setter,属性可以通过反射设置,所以它不是不可变的。在此示例中,这对您来说可能是问题,也可能不是问题。就我个人而言,我更喜欢你的方法,因为它绝对不变,其他编码人员可以轻松推断出意图。
无论如何,你因为你的数据是不可变的,你的错误也是不可变的,所以永远不会改变。您遵循的模式是正确的。
您检查构造函数中的错误是正确的,因为您不想创建有错误的不可变对象。
[我本想把它写成评论,而不是答案,但我 ^H^H 的声誉差了 1 分。我现在就把它留在这里。]
C# 6 支持不可变的 getter-only auto 属性,因此如果您使用的是 Visual Studio 2015,您可以像这样进一步重构:
public int Id { get; }
public Person(int id, string firstName, string lastName)
{
if (id < 0)
throw new ArgumentOutOfRangeException("id");
Id=id;
}
我已将示例限制为 ID
属性。构造函数验证该值并将其分配给 属性(如果有效),并且 属性 是完全不可变的,而且您不必显式声明支持字段。 Resharper website.