C#在不可变类中抛出异常

时间:2015-07-09 09:24:40

标签: c# class exception immutability

我正在阅读here关于在C#中创建不可变类的内容。有人建议我以这种方式使我的课程具有真正的不变性:

private readonly int id;

public Person(int id) {

    this.id = id;
}

public int Id {
    get { return id; }
}

我明白了,但如果我想做一些错误检查怎么办,怎么能抛出异常呢?鉴于以下课程,我只能这样做:

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

这是正确的做法吗?如果没有,我将如何完成在不可变类中抛出异常?

4 个答案:

答案 0 :(得分:3)

我会内联这两个私有方法:

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

答案 1 :(得分:1)

如果使用私有setter,则可以通过反射设置属性,因此它不是不可变的。在此示例中,这可能是也可能不是您的问题。就个人而言,我更喜欢你的方法,因为它绝对是不可变的,其他编码员可以轻松地推断出意图。

在任何情况下,由于您的数据是不可变的,您的错误也是不可变的,因此永远不会改变。您遵循的模式是正确的。

检查构造函数中的错误是正确的,因为您不希望创建有错误的不可变对象。

[我本来希望把它写成评论,而不是答案,但我是^ H ^ H在声誉上只有1分。我现在就把它留在这里。]

答案 2 :(得分:1)

C#6支持不可变的仅限getter的自动属性,因此如果您使用的是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上的重构演示,可以很好地记录下来。

答案 3 :(得分:0)

如果有必要将字段标记为只读 - 除了在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
   }
}

在ctor:

public Person(int id, string name) {
    CheckValue(id, "id");
    CheckValue(name, "name");
    this.Id = id;
}