C# 复杂类型的方法参数验证
我非常喜欢方法中的论证验证。在我的代码中通常可以看到这样的情况:C# 复杂类型的方法参数验证,c#,validation,arguments,complextype,C#,Validation,Arguments,Complextype,我非常喜欢方法中的论证验证。在我的代码中通常可以看到这样的情况: public void Foo(string someString) { #region parameter validation if(string.IsNullOrWhiteSpace(someString)) throw new ArgumentNullException("someString"); // Let's ignore the .NET 4.6 nameof-fe
public void Foo(string someString)
{
#region parameter validation
if(string.IsNullOrWhiteSpace(someString))
throw new ArgumentNullException("someString");
// Let's ignore the .NET 4.6 nameof-feature for the sake of this question.
#endregion
// do things
}
现在让我们假设该方法不接受字符串,而是一个复杂对象。假设我希望该方法验证是否设置了这个复杂对象的属性。正确的方法是什么?到目前为止,我是这样做的:
public void Foo(Person person)
{
#region parameter validation
if(person == null)
throw new ArgumentNullException("person");
if(string.IsNullOrWhiteSpace(person.Name))
throw new ArgumentNullException("person.Name");
#endregion
// do things
}
if (person == null)
throw new ArgumentNullException (nameof (person));
// Validates only 1 property.
if (!person.Validate (nameof (person.MyProperty)))
DisplayErrors (person);
// Validates all properties.
if (!person.Validate ())
DisplayErrors (person);
int? length = customers?.Length;
这方面有什么最佳实践吗?
ArgumentNullException
是正确的方法吗?是否建议这样检查属性?在我的例子中,我所有的模型都有一个验证方法,这样我就调用它,如果它返回false(这意味着我有错误),我调用另一个方法从中获取错误并向用户显示,类似这样:
public void Foo(Person person)
{
#region parameter validation
if(person == null)
throw new ArgumentNullException("person");
if(string.IsNullOrWhiteSpace(person.Name))
throw new ArgumentNullException("person.Name");
#endregion
// do things
}
if (person == null)
throw new ArgumentNullException (nameof (person));
// Validates only 1 property.
if (!person.Validate (nameof (person.MyProperty)))
DisplayErrors (person);
// Validates all properties.
if (!person.Validate ())
DisplayErrors (person);
int? length = customers?.Length;
其背后的原因是,在大多数情况下,当用户填写信息时,我必须允许模型无效。我将这些方法与用于WPF的接口INotifyDataErrorInfo一起使用,以使UI在出现错误时能够刷新
当一个方法或操作只需要对象的一部分时,facade模型是一种解决方案,您可以在其中创建一个新类来封装更复杂的类,facade类也有Validate方法来进行部分验证。对我来说,对于99%的情况来说,这听起来像是一个设计问题。没有名字的人有效吗?我想不是。如果不是,为什么你允许一个人被创造而没有一个人?如果你(出于某种原因)允许它,那么维护它自己的“有效性”不是对象的责任吗?我倾向于用数据注释来标记元数据,这些标记是必需的状态,并且简单地检查传入的人是“有效的” 您可以考虑。当然,如您所示,检查是验证参数的一种非常好的方法。。。假设您只验证正在使用的字段。验证未引用的字段会带来麻烦(除非这是该方法的明确目的) 最新版本的C#提供了一个“?”操作符,它允许您执行以下操作:
public void Foo(Person person)
{
#region parameter validation
if(person == null)
throw new ArgumentNullException("person");
if(string.IsNullOrWhiteSpace(person.Name))
throw new ArgumentNullException("person.Name");
#endregion
// do things
}
if (person == null)
throw new ArgumentNullException (nameof (person));
// Validates only 1 property.
if (!person.Validate (nameof (person.MyProperty)))
DisplayErrors (person);
// Validates all properties.
if (!person.Validate ())
DisplayErrors (person);
int? length = customers?.Length;
或
这允许您保护自己不受空引用异常的影响,在某些情况下可能比预验证更可取
哦,运行代码分析工具通常会在验证参数之前使用参数进行标记,所以至少这些工具认为这是一种最佳实践
更新(回应评论):
这在很大程度上取决于你想做什么。如果您正在创建一个API,并且希望返回尽可能最好的错误消息,那么您所建议的可能是您所能做到的最好的。这假设您主要关心与API的使用者的通信
如果您的主要兴趣是确保提供给您的方法的数据是有效的,并且不会导致您的方法行为不良(CYA),那么您应该查看合同。契约的优点是提供了post条件(确保您的方法不会返回意外的结果)和不变条件(确保没有更改您不想更改的内容)。契约既可以提供静态(编译时)保证,也可以提供运行时保证。有一种相当细粒度的方法来管理合同选项。运行时契约冲突会产生人们可能不太熟悉的契约异常。用户也可能不熟悉编译时的投诉。
这假设您主要关心的是确保正确的操作,或者不会因为滥用API而受到指责
您可以同时使用这两种方法,例如使用契约进行编译时检查,并使用更简单的“如果”检查进行运行时检查 似乎没有官方正确的方法来做这件事,所以我会坚持我自己的方法,那就是
if(myComplexObject.SomeProperty == null)
throw new ArgumentNullException("myComplexObject.SomeProperty");
打电话的人会立即知道出了什么问题
请注意,我没有使用operator的名称,因为这同样只会给我SomeProperty
,而不是myComplexObject.SomeProperty
。我可以做nameof(myComplexObject)+““+nameof(myComplexObject.SomeProperty)”,但我需要再考虑一下,乍一看是不对的
以下是我认为其他方法比我的更糟糕的原因:
有人说我应该在模型中进行验证,我不会这样做,因为模型不知道如何对不知道的方法验证对象。如果是整体对象实例的有效性,当然,请将其保留在模型中,或者如果您要求我甚至是业务层进行业务验证,但情况并非如此
其他人说我可以在C#中使用空合并操作符(?。
),我也不会这样做,因为这不是“如果为空,那么就使用该”场景,而是“如果为空,我不会做任何事情,您应该为给我空值感到难过,修复您的代码”
还有代码契约,它提供了一种验证参数的好方法,并有一些真正好的好处,但最终还是出现了同样的问题:我如何告诉调用方一个复杂对象的哪个值是无效的?他需要知道参数Y的属性X是错误的,尽管代码契约不是唯一的回答我的问题,我想我的答案也可以用在那里,如果同样的情况发生的话
然后有人建议我不应该在那个地方验证对象的属性,而应该在需要的函数中验证。问题是,该属性几乎不作为单独的值传递给另一个函数。另外,它会与接口的概念发生冲突。如果检查真的很重要,请您与n是否在用于运行这些测试的每个类中放置一个静态方法?是否可能创建一个需要对属性运行测试的方法的接口?ArgumentNullException
假设参数为null
,而不是空白字符串。@kailanjian:否,因为验证