1

我有一个看起来如下的类:

public class StackOverflowQuestion {

    private string _question;

    public string Question {
        get { return _question;  }
        set { _question = value; }
    }

    public StackOverflowQuestion(string question) {
        _question = question;
    }

    public override string ToString() {
        return _question;
    }
}

现在,值“question”不允许为 null 或空,并且应该通过 ArgumentNullException 通知用户 - 但是应该将它扔到哪里呢?根据“快速失败”原则 -> 无处不在。

public class StackOverflowQuestion {

    private string _question;

    public string Question {
        get { return _question;  }
        set { 
           if(!String.IsNullOrEmpty(value))
                _question = value
            else throw new ArgumentNullException("value");
        }
    }

    public StackOverflowQuestion(string question) {
        if(!String.IsNullOrEmpty(question))
            _question = question;
        else throw new ArgumentNullException("question");
    }

    public override string ToString() {
        if(!String.IsNullOrEmpty(_question)) return _question;
        else throw new ArgumentNullException("_question");
    }
}

现在这显然是荒谬的,而且极其重复。但这似乎是正确的:如果该值是通过 .ctor 设置的,则在短暂检查后直接失败。当它通过属性设置时,它在短暂检查后直接失败..但是谁期望设置器出现异常?当我输出字符串时,我期待一个字符串,而不是早就应该发生的事情的例外,但同样:如果它错了,它应该尽快失败,即使“很快”已经很晚了。

那么,唯一的异常处理应该在哪里进行呢?我是在要求“最佳实践”,还是这是一种品味?

4

3 回答 3

3

由于 _question 是私有的,因此无需在 ToString() 中检查它是否为空(除非您只是在检查自己的代码)。

您可以通过让构造函数使用属性设置器来避免在构造函数中进行检查。因此,我建议:

public class StackOverflowQuestion {

    private string _question;

    public string Question {
        get { return _question;  }
        set { 
           if(string.IsNullOrEmpty(value))
                // to make this more transparent when thrown through the constructor, it might
                // be preferable to throw a real error message like "Question: cannot be null or empty"
                throw new ArgumentException("value");
           this._question = value;
        }
    }

    public StackOverflowQuestion(string question) {
        this.Question = question;
    }

    public override string ToString() {
        return this.Question;
    }
}

需要注意的几点: 1. 你应该抛出ArgumentException而不是ArgumentNullException空字符串(如果你愿意,你可以做 2 次检查并且仍然抛出ArgumentNullException空值)。2. 虽然该方法使用较少的代码,但一个缺点是用户得到的错误消息比将 null 传递给构造函数时稍差,因为故障发生在 2 层而不是 1 层。

于 2013-07-18T11:39:48.597 回答
2

您只需要测试一次- 在您设置变量的单个位置,更改构造函数以使用该属性:

public class StackOverflowQuestion
{
    private string _question;

    public string Question
    {
        get { return _question; }
        set
        { 
           if (String.IsNullOrEmpty(value))
           {
               throw new ArgumentException("Question cannot be null or empty",
                                           "value");
           }
           _question = value;
        }
    }

    public StackOverflowQuestion(string question)
    {
        Question = question;
    }

    public override string ToString()
    {
        return Question;
    }
}

这里的一个缺点是“坏参数”名称将是value而不是question在构造函数中为 null 时,但我认为这是值得付出的代价。另一种方法是使用消息,而不指定参数名称。

可能想从 中分离出来nullempty这样你就可以ArgumentNullException在它为空时抛出 an - 但你不应该ArgumentNullException在它为空时抛出。

您在获取值时不需要执行任何检查,因为您知道它永远不会为 null 或为空,因为您正在阻止它以这种方式设置。

您还应该考虑是否可以使类不可变,此时您只需要在构造函数中进行测试,因为不会有 setter ...

于 2013-07-18T11:39:41.580 回答
1

我宁愿让它不可变:

public class StackOverflowQuestion
    {
        public string Question
        {
            get; private set;
        }

        public StackOverflowQuestion(string question)
        {
            if (String.IsNullOrEmpty(question))                
              throw new ArgumentNullException("question");

            Question = question;
        }

        public override string ToString()
        {
            return Question;
        }
    }
于 2013-07-18T11:52:28.420 回答