我有一个看起来如下的课程:
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设置的,它会在短暂检查后直接失败。当它通过属性设置时,它会在短暂检查后直接失败..但谁会在setter上预期异常?当我输出字符串时,我期待一个字符串,而不是很久以前应该发生的事情的例外,但是再次:如果它错了,它应该尽快失败,即使'很快'已经很晚了。
那么,唯一的异常处理应该在哪里完成?我要求“最佳实践”,还是这是一种品味的东西?
答案 0 :(得分:3)
由于_question是私有的,因此无需在ToString()中检查它是否为null(除非您只是检查自己的代码)。
您可以通过让构造函数使用属性setter来避免检查构造函数。因此,我建议:
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.虽然该方法使用较少的代码,但一个缺点是用户获取的错误消息比将它们传递给构造函数时更差,因为失败发生在2级而不是1级。
答案 1 :(得分: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时,但我认为这是一个值得付出的代价。另一种方法是只来使用该消息,而不是指定参数名称。
希望将null
与empty
区分开来,以便在ArgumentNullException
为空时抛出ArgumentNullException
- 但你不应该抛出{{1}}当它只是空的时候。
当获取值时,您不需要执行任何检查,因为您知道它永远不会为null或为空,因为您正在阻止它以这种方式设置。
您还应该考虑是否可以使该类不可变,此时您只需要在构造函数中进行测试,因为没有setter ...
答案 2 :(得分: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;
}
}