C#属性 - 设置问题

时间:2009-05-27 14:47:58

标签: c#

我在C#工作,我开始玩房产了。我不知道的一件事是什么是为类属性的set访问器设置逻辑以及如何处理错误的最佳方式/位置。

例如,假设我有这个(基础)课程:

class Person
{
    private int _Age = 18;

    public Person()
    {

    }

    public int Age
    {
        get
        {
            return _Age;
        }
        set
        {
            _Age = value;
        }
    }
}

现在说我对Age属性有一个要求,0&lt;年龄< 100.我在哪里列出这个逻辑?

我应该把它放在房产里吗?

public int Age
    {
        get
        {
            return _Age;
        }
        set
        {
           if (value < 0 || value > 99)
               // handle error
           else
               _Age = Convert.ToInt32(value);
        }
    }

或通过创建Person对象的类?

static void Main(string[] args)
{
   Person him = new Person();
   int NewAge = -10;

   if (NewAge < 0 || NewAge > 100)
      // handle error 
   else
      him.Age = NewAge;
}

现在如果NewAge出现问题(它不符合我的约束条件)怎么办?我应该创建一个自定义异常并抛出它吗?我应该打印一条消息说是否提供有效年龄?

我做了一些谷歌搜索,我找不到任何能完全回答我问题的内容。我需要一本书: - /

10 个答案:

答案 0 :(得分:23)

使用属性设置器,就是出于这个原因(向字段添加功能)。

如果传入超出范围的值,您可以抛出ArgumentOutOfRangeException或仅将其设置为最小(或最大)值,但这取决于您的流程要求。

答案 1 :(得分:11)

我会像这样实现它:

public int Age
{
    get
    {
        return _Age;
    }
    set
    {
       if (IsValidAge(value))
           _Age = value;
       else
           throw new ArgumentOutOfRangeException("value", string.Format("value should be between {0} and {1} inclusive.", MinAge, MaxAge));
    }
}

private bool IsValidAge(int age)
{
    return (age >= MinAge && age <= MaxAge);
}

有几点需要注意:

  • 不要更改其值而不是抛出异常,这是意外行为。
  • .NET框架在setter中引发了Argument *异常,所以我想说遵循这种做法是个好主意。在这种情况下,ArgumentOutOfRangeException是完美的,IMO。
  • 当引用异常消息和xml文档中的参数时,标准是调用参数“value”,而不是属性的名称。
  • 我建议您在班级中使用MinAge和MaxAge作为私有功能,不要陷入带有范围边界的硬编码错误信息的陷阱,没有什么比被告知“5无效,请输入一个数字更糟糕的1和10“当有人稍后更改规范但忘记更新字符串时。

答案 2 :(得分:6)

如果不符合要求,您将需要将逻辑放入setter并抛出异常。但是,您还需要创建一个IsValidAge静态方法,以便创建Person的类可以检查年龄值,而不仅仅是看它是否抛出异常。或者,您可以使用MinAge和MaxAge属性,因此调用代码可以检查它们即将设置的年龄是否介于它之间。

不要创建自定义异常类型,使用ArgumentOutOfRangeException或类似的东西。

答案 3 :(得分:3)

把它放在财产中。这是物业的主要目的之一!

考虑到您将责任放在最低级别。您提供的示例不需要 value 参数以外的任何内容来做出决定。它甚至不依赖于同一类的其他成员。没有理由让班上的其他人知道Age属性的有效性如何,当然也没有理由让任何其他代码知道它。

答案 4 :(得分:3)

您可能需要查看IDataErrorInfo接口。

通过在类中实现此接口,您可以将该类打开到可以从其他错误信息中受益的其他机制。

答案 5 :(得分:1)

我将扮演魔鬼的拥护者并给出答案,为什么你不想把它放在制定者中。有很多情况下,您希望能够设置Age属性的值,并在以后询问该对象在整体意义上是否有效。

例如,保持您的财产简单:

public int Age { get; set; }

然后,当传入无效值时,您可以使用一些IsValid函数来指示相关对象是否正常。这非常有用,因为除了简单的年龄限制之外,您可以进行更复杂的验证。

bool IsValid()
{
   if (Age < 0 || Age > 99)
     return false;
}

对于像这样简单的东西没有太多好处,但也要考虑你可以在持久层中使用它,这样你就可以确保任何无效的对象永远不会被持久化。在这些情况下,您不一定要抛出异常。

还要考虑这个:

DateTime StartDate { get; set; }
DateTime EndDate { get; set;}

bool IsValid()
{
   return StartDate > EndDate      
}

这只是伪代码,但你明白我的观点。这是你在setter中无法做到的事情,或者至少是不可维护的。

答案 6 :(得分:0)

使用setter,并使用ArgumentOutOfRangeException,正如其他人已经说过的那样。

有时你会调用另一个方法,通常是OnAgeChanged(int age),你可以在那里执行验证(可能是对另一个方法的调用,所以你可以在任何地方使用它)并调用一个事件处理程序,如果连接,可能有其他逻辑应用取决于您如何使用您的对象。在您的方案中可能没有必要,但对于属性来说这是很常见的。特别是如果您要更新表单 - 表单将挂钩到AgeChanged事件以进行更新。

答案 7 :(得分:0)

如果您正在进行因式设计(即,对象的状态在对象生命期内的所有点都有效),那么您可能需要在构造函数中设置并检查它。在组件设计中,您需要在使用该属性的方法中进行检查,例如

class Person
{
  public int calculateTimeToExpiration()
  {
  if (Age < 0 || Age > 100)
     //throw
  }
}

静态void main是客户端代码,不是业务逻辑的好地方。

答案 8 :(得分:0)

感谢您的回答!如果你好奇这里是我改变我的小例子(尽管现在更详细)到(基于评论)。

class Person
{
    private string _FirstName = "Joe";
    private string _LastName = "Smith";
    private int _Age = 18;

    private const int MinAge = 1;
    private const int MaxAge = 99;

    public Person()
    {

    }

    public Person(string FirstName, string LastName, int Age)
    {
        this.FirstName = FirstName;
        this.LastName = LastName;
        this.Age = Age;
    }

    public string FirstName
    {
        get
        {
            return _FirstName;
        }
        set
        {
            _FirstName = value;
        }
    }

    public string LastName
    {
        get
        {
            return _LastName;
        }
        set
        {
            _LastName = value;
        }
    }

    public int Age
    {
        get
        {
            return _Age;
        }
        set
        {
            if (IsValidAge(value))
                throw new ArgumentOutOfRangeException("value","Please enter a positive age less than 100.");
            else
                _Age = value;
        }
    }

    private bool IsValidAge(int age)
    {
        return (age < MinAge || age > MaxAge);
    }

    public override string ToString()
    {
        if (Age == 1) 
            return "My name is " + FirstName + " " + LastName + " and I am " + Age + " year old.";
        else
            return "My name is " + FirstName + " " + LastName + " and I am " + Age + " years old.";
    }  
}

static void Main(string[] args)
    {
        Person him, her;

        try
        {
            him = new Person("Joe Bob", "McGruff", 1);
            Console.WriteLine(him);
        }
        catch (ArgumentOutOfRangeException range)
        {
            Console.WriteLine(range.Message);
        }

        try
        {
            her = new Person();
            her.Age = -5;
            Console.WriteLine(her);
        }
        catch (ArgumentOutOfRangeException range)
        {
            Console.Write(range.Message);
        }

        Console.ReadKey();
    }

答案 9 :(得分:0)

您的更新答案很好,但您应该在您的setter / validation中清理一小段

 public int Age
    {
        get
        {
            return _Age;
        }
        set
        {
            if (!IsValidAge(value))
                throw new ArgumentOutOfRangeException("Age","Please enter a positive age less than 100.");

             _Age = value;
        }
    }

    private bool IsValidAge(int age)
    {
        return (age > MinAge && age < MaxAge);
    }