C# 保护值与数据的方法?
有人知道防止无效数据而不是无效值的技术或方法吗?我知道这似乎是一个奇怪的区别,但请容忍我 最近我遇到了一个问题,关于如何更好地防止无效变量值。我们目前正在将异常包装到一个C# 保护值与数据的方法?,c#,validation,design-patterns,C#,Validation,Design Patterns,有人知道防止无效数据而不是无效值的技术或方法吗?我知道这似乎是一个奇怪的区别,但请容忍我 最近我遇到了一个问题,关于如何更好地防止无效变量值。我们目前正在将异常包装到一个Guard类中,但建议进一步将异常包装到扩展中。换言之,这: int myVar = 0; // Basic if (myVar < 1) throw new InvalidArgumentException("myVar cannot be less than 1"); // Guard -- wraps the a
Guard
类中,但建议进一步将异常包装到扩展中。换言之,这:
int myVar = 0;
// Basic
if (myVar < 1) throw new InvalidArgumentException("myVar cannot be less than 1");
// Guard -- wraps the above exception
Guard.AgainstValuesLessThan(1, myVar, nameof(myVar), "Value cannot be less than 1");
// Extension -- wraps the above guard
myVar.EnsureValid();
所以我想找到一个更好的方法,这就是我绊倒的地方。到目前为止,我遇到的问题是,从根本上说,我的问题是,我在考虑作为数据保护什么(“我需要保护坏的客户ID”),但我在保护值(“我需要确保这个整数至少为1”)
我不知道接下来该怎么办。我觉得这可能是那些“我不知道它存在,但这很有帮助”的案例之一
有人对看什么有什么建议吗?到目前为止,我有三个想法,我不确定其中一个是否比另一个好,或者是否有某种方法将它们结合起来以实现我的目标:
- 代码合同
- 我缺少一些设计模式
- 其他一些技巧或技巧(例如属性?
?)InvokerParameterName
最后一点注意:我知道ReSharper提供了一个注释库,但不是我团队中的每个人都使用ReSharper(包括我),所以我不想依赖它。从可维护性的角度来看,我认为一些OOP/设计模式是最好的方法。我同意你的看法,如果不是所有开发人员都需要使用ReSharper,那么就尝试将它嵌入到代码中 您拥有Guard子句的方式简单且易于重用;我不会改变这一点。如果我理解正确的话,您是在防范业务规则,而不一定是基本价值观。您希望封装您的业务规则“不能具有小于1的ID”,这绝对是一个值得警惕的好规则 我建议您考虑以下几个选项:
公共雇员(内部id)
{
防范价值小于(1,id,姓名(id),“员工id不能小于1”);
}
如果您在代码中的其他地方使用该对象,您肯定知道它处于有效状态,因为您成功地构造了该对象。这也解决了记住调用调用方函数/类中的Guard子句的问题
我倾向于首先选择选项1,因为它可以帮助您集中精力创建具有有效状态的对象,这是一个很好的原则。如果你需要更多的例子,或者我错过了你的问题,请告诉我,我可以给出更好的答案:)这并不能完全回答你的问题,但这正是我在评论中所说的:
public static class AssertValid
{
public static void TestAssertValid()
{
var customerId = 0;
AssertValid.MinimumFor(customerId, 1, nameof(customerId));
}
public static void RangeFor<T>(T variableValue, T min, T max, string varName,
string message = "Variable {0} outside of range {1} to {2} in function {3}",
[CallerMemberName] string inFunc = "") where T : IComparable
{
if (variableValue.CompareTo(min) < 0 || variableValue.CompareTo(max) > 0)
{
var msg = string.Format(message, varName, min, max, inFunc);
throw new ArgumentOutOfRangeException(varName, variableValue, msg);
}
}
public static void MinimumFor<T>(T variableValue, T min, string varName,
string message = "Variable {0} less than minimum of {1} in function {2}",
[CallerMemberName] string inFunc = "") where T : IComparable
{
if (variableValue.CompareTo(min) < 0)
{
var msg = string.Format(message, varName, min, inFunc);
throw new ArgumentOutOfRangeException(varName, variableValue, msg);
}
}
public static void MaximumFor<T>(T variableValue, T min, string varName,
string message = "Variable {0} greater than maximum of {1} in function {2}",
[CallerMemberName] string inFunc = "") where T : IComparable
{
//...
}
public static void StringLengthRangeFor(string variableValue, int min, int max, string varName,
string message = "Length of string variable {0} outside of range {1} to {2} in function {3}",
[CallerMemberName] string inFunc = "")
{
if (variableValue.Length < min || variableValue.Length > max)
{
var msg = string.Format(message, varName, min, max, inFunc);
throw new ArgumentOutOfRangeException(varName, variableValue, msg);
}
}
public static void StringLengthMinFor(string variableValue, int min, int max, string varName,
string message = "Length of string variable {0} less than {1} characters in function {2}",
[CallerMemberName] string inFunc = "")
{
if (variableValue.Length < min)
{
var msg = string.Format(message, varName, min, inFunc);
throw new ArgumentOutOfRangeException(varName, variableValue, msg);
}
}
public static void StringLengthMaxFor(string variableValue, int max, string varName,
string message = "Length of string variable {0} greater than {1} characters in function {2}",
[CallerMemberName] string inFunc = "")
{
//...
}
public static void StringLengthPatternFor(string variableValue, string regexPattern, string varName,
string message = "String variable {0} does not match acceptable pattern in function {1}",
[CallerMemberName] string inFunc = "")
{
//... Use ArgumentException
}
}
最终会出现如下异常:
System.ArgumentOutOfRangeException: 'Variable customerId less than minimum of 1 in function TestAssertValid
Parameter name: customerId
Actual value was 0.'
您可能要做的另一件事是将其变成一个可实例化的类,所有方法都是实例方法。创建类的一个实例,对该实例执行所有断言,最后断言一切正常。如果不是,则将所有故障一起吐出(在一个异常中)
这样,所有测试都会在抛出异常之前完成。那么,使用这些是代码契约式的先决条件吗?在继续您的逻辑之前,您想断言一组先决条件吗?这些是您“保护”的局部变量,而不是字段或属性,对吗?您看过(现在已弃用的)代码契约内容和ComponentModel注释吗?另一个需要注意的是MSTest Assert类(我比较积极,我喜欢断言满足了前提条件,而不是防止坏数据)。您可能还希望将其混合在()中,以便可以在异常中获得一些调用方信息。如果是我,可能会有一些静态类(比如
AssertValid
),其方法有“Range,
Minimum,
Maximum`(其中T:IComparable
)、StringLengthRange、StringLengthMinimum、StringLengthMaximum、StringPattern(正则表达式),等等。你真正看到的是一种专门化类型的方法,而不是你在C#中所能做的。您希望创建一个CustomerId
或EmployerId
类型,以了解作为客户或雇主的本质(您可以使用一些语言,例如oCaml,参见@EricLippert的博客)。但是,你不能在C#中这样做。你可以创建像Customer和Employer这样的类型,让他们声明他们的ID符合规范,但我认为你不能再进一步了。这与我在阅读问题时的想法非常接近+1.
var customerId = 0;
AssertValid.MinimumFor(customerId, 1, nameof(customerId));
System.ArgumentOutOfRangeException: 'Variable customerId less than minimum of 1 in function TestAssertValid
Parameter name: customerId
Actual value was 0.'