C# CuttingEdge.Conditions 验证帮助类库 文档翻译

时间:2021-09-30 20:26:27

项目主页: https://archive.codeplex.com/?p=conditions

作者博客关于项目的文档(翻译原文): https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=38#Designed_Behavior

前言

CuttingEdge.Conditions 可帮助开发人员在其.NET 3.5代码库中编写前后条件验证。

编写这些验证很容易,它提高了代码的可读性和可维护性。

警告:

此帖子基于CuttingEdge.Conditions 的 预览版本发布。虽然库的大多数概念和行为都是相同的,但最终版本有一些更改。

其中最明显的是删除了 Requires() 和 Ensures{} 方法的扩展方法行为。

请注意,不再支持以下语法:c.Requires().IsNotNull() ,建议的语法是 Condition.Requires(c).IsNotNull()。

请在阅读本文时牢记这一点。

编写前置条件验证可以提高代码质量。具有验证的代码更易于理解,并允许开发人员更快地发现错误,主要是在开发期间发现这些问题而不是在调试期间。

然而,编写前置条件验证一直是编程中不太好处理的地方。编写它需要时间,我和许多开发人员(甚至是我尊重的人)都会跳过编写它们。

跳过前置条件验证将导致代码更难以使用并且可能被滥用。它允许开发人员传递一些无效的方法参数,

这会导致意外的程序行为以及来自调用堆栈内部的那些可怕的 NullReferenceException 等异常。它会导致更多的错误,从而花费更多的时间进行调试。

CuttingEdge.Conditions 库试图降低编写前置条件验证的障碍并使代码更具可读性,从而产生更好的代码,更少的错误和更短的开发周期。

要了解 CuttingEdge.Conditions 如何尝试实现这一目标,让我们首先看看我们可能每天编写的一些代码。

这是一个前置条件验证的例子,旧的实现方式:

 void TheOldFashionWay(int id, IEnumerable<int> col,
DayOfWeek day)
{
if (id < )
{
throw new ArgumentOutOfRangeException("id",
String.Format("id should be greater " +
"than 0. The actual value is {0}.", id));
} if (col == null)
{
throw new ArgumentNullException("col",
"collection should not be empty");
} if (col.Count() == )
{
throw new ArgumentException(
"collection should not be empty", "col");
} if (day >= DayOfWeek.Monday &&
day <= DayOfWeek.Friday)
{
throw new InvalidEnumArgumentException(
String.Format("day should be between " +
"Monday and Friday. The actual value " +
"is {0}.", day));
} // Do method work
}

对于这样一些简单的验证,这是一些非常多的代码!

以下是 CuttingEdge.Conditions 的实现方式:

 //旧的调用方式
void TheConditionsWay(int id, IEnumerable<int> col,
DayOfWeek day)
{
id.Requires("id").IsGreaterThan();
col.Requires("col").IsNotEmpty();
day.Requires("day").IsInRange(DayOfWeek.Monday, DayOfWeek.Friday); // Do method work
} //新的调用方式
13 private static void Main(string[] args)
14 {
15   var id = ;   Condition.Requires(id)
18     .IsGreaterThan()
    .IsInRange(, ) // ArgumentOutOfRangeException on failure
.IsNotEqualTo(,"编号不能为 1000"); // throws ArgumentException on failure   var text = "hahahahhaha";   Condition.Requires(text, "字符串") 26     .StartsWith("h") // throws ArgumentException on failure
    .EndsWith("a") // throws ArgumentException on failure
.Evaluate(text.Contains("abc") || text.Contains("cba")); // arg ex
}

这是完全不同的,不是吗?

它不仅代码少得多; 它也非常易读。请注意,两种方法都有完全相同的合同; 两种方法抛出完全相同的异常!

除了这些正常的前提条件检查,CuttingEdge.Conditions还允许您进行后置条件检查。

与先决条件不同,违反后置条件纯粹是内部原因。

它可以被认为是一个错误。在这种情况下抛出ArgumentException会明显混淆使用该代码的开发人员。由于存在这种差异,CuttingEdge.Conditions 对违反后置条件的代码总会抛出一个 PostconditionException 。

以下是后置条件检查的示例:

 void TheConditionsWay(int id, IEnumerable<int> col,
DayOfWeek day)
{
id.Requires("id").IsGreaterThan();
col.Requires("col").IsNotEmpty();
day.Requires("day").IsInRange(DayOfWeek.Monday, DayOfWeek.Friday); // Do method work
}

后置条件示例显示了两个有趣的事情。

首先,使用Ensures扩展方法来启动后置条件验证。

其次,方法调用可以以流畅的方式链接,如 IsNotNull 和 IsOfType 方法所示。

API

CuttingEdge.Conditions API有许多验证方法,

可以轻松满足98%的验证需求。目前有51种不同检查的88种扩展方法。API可分为七组:

  • 入口点方法
  • 空检查方法
  • 键入检查方法
  • 比较检查
  • 集合检查
  • 字符串检查
  • 自定义验证

下面我将列出 CuttingEdge.Conditions 当前beta 1版本中的所有方法。

方法的数量可能会随着时间的推移而增长,如果您认为缺少验证,请在此处或在Codeplex上发表评论。我会考虑将它们添加到库中。

另请注意,只需将扩展方法放在您自己的项目中,您就可以使用自己的方法扩展API。

Entry point methods 入口点方法

入口点方法用于启动验证。可以在每个类型的每个变量上调用这些扩展方法。目前有两种方法:

Requires (2 重载)

Ensures (3 重载)

Requires 可以用来写先决条件,它在验证失败时抛出 ArgumentException 或 它的一个子类。

Ensures 可以用来写后置条件。它将抛出 PostconditionException。

空检查方法

如果参数需要值或不需要值,则可以使用空检查。可以对引用类型和 Nullable <T> 结构执行这些检查。

有两种方法:

IsNotNull(2 重载)

IsNull(2 重载)

键入检查方法

有两种方法可用于类型检查:

IsOfType

IsNotOfType

比较检查

比较检查验证参数是否等于某个其他值或在给定范围内。

它们可以在Nullable <T>结构和所有实现 IComparable 的类型上执行。目前有12种方法:

IsEqualTo(3 重载)

IsNotEqualTo(3 重载)

IsGreaterThan(3 重载)

IsNotGreaterThan(3 重载)

IsGreaterOrEqual(3 重载)

IsNotGreaterOrEqual(3 重载)

IsInRange(3 重载)

IsNotInRange(3 重载)

IsLessThan(3 重载)

IsNotLessThan(3 重载)

IsLessOrEqual(3 重载)

IsNotLessOrEqual(3 重载)

集合检查

集合检查可用于(至少)实现 IEnumerable 的类型。目前有18种方法:

Contains(2 重载)

DoesNotContain(2 重载)

ContainsAll(2 重载)

DoesNotContainAll(2 重载)

ContainsAny(2 重载)

DoesNotContainAny(2 重载)

IsEmpty

IsNotEmpty

HasLength

DoesNotHaveLength

IsLongerThan

IsNotLongerThan

IsLongerOrEqual

IsNotLongerOrEqual

IsShorterThan

IsNotShorterThan

IsShorterOrEqual

IsNotShorterOrEqual

字符串检查

有一组单独的方法可以验证字符串,目前有16种方法:

Contains

DoesNotContain

StartsWith(2 重载)

DoesNotStartWith(2 重载)

EndsWith(2 重载)

DoesNotEndWith(2 重载)

HasLength

DoesNotHaveLength

IsEmpty

IsNotEmpty

IsNullOrEmpty

IsNotNullOrEmpty

IsLongerThan

IsLongerOrEqual

IsShorterThan

IsShorterOrEqual

自定义验证

对于使用上述方法无法完成的所有检查,使用 Evaluate 方法重载是一种解决方案。

第一个重载检查并返回一个布尔值,将在它等于false时抛出异常。

第二个重载运行一个返回布尔值的指定 Expression。这允许开发人员定义lambda表达式。这两个重载允许字面表达您想要的任何前置或后置条件。有一种方法:

Evaluate(2次重载)

以下是使用Evaluate的两个示例:

 // Evaluate with boolean
s.Requires("s").Evaluate(
s.StartsWith("hello") || s.EndsWith("world"));
// Evaluate using a lambda expression
s.Requires("s").Evaluate((str) =>
str.StartsWith("hello") || str.EndsWith("world"));

这两个例子看起来很多,但它们实际上是完全不同的。

第一个示例使用布尔参数,检查速度非常快。但是,它在失败时缺少一个好的异常消息。

与 lambda 表达式的重载恰恰相反。

它必须在每次调用时编译给定的表达式,因此不应该在代码的性能敏感部分中使用它。但是在使用时,能够抛出一个具有描述性的异常消息:

'(str.StartsWith("hello") || str.EndsWith("world"))' should hold for s. The actual value is 'world hello'. Parameter name: s。

CuttingEdge.Conditions API经过精心设计,

我希望它以最直观的方式运行(您可以在这里阅读更多关于设计过程和一些背景知识)。

下面是我做出的一些设计决策以及开发人员必须编写的代码的后果。如果这种行为很奇怪并且需要改变,请告诉我。

空值被认为小于任何非空值。

这与在.NET框架中验证对象的方式一致。以下代码行显示此行为:

 int? i = null;
// Next check will pass, because null is
// smaller than Int32.MinValue.
i.Requires().IsLessThan(Int32.MinValue);

包含空引用的集合参数被视为包含零元素。

对于用户来说,这种行为可能看起来很奇怪。当检查空引用时,库的用户可能希望 API 抛出 ArgumentNullException 。

但是在某些情况下,抛出异常将不是预期的行为,因此会导致API不一致。除此之外,选择空引用将会始终失败,将限制该 API 的有用性。

在某些情况下,用户可能会发现空引用是有效值。即,用户可以如下定义前提条件:“ 集合应该具有少于五个元素并且可以是空引用”。

他无法使用集合方法的 API 来表达这个前提条件。他将*使用 Evaluate 机制,该机制显然不太可读,并且不会抛出描述性的异常消息。

以下示例显示了设计的行为:

 IEnumerable c = null;
// Both HasLength and IsEmpty checks will
// pass, because c equals null.
c.Requires().HasLength();
c.Requires().IsEmpty();
// IsShorterThan will pass, because c is
// clearly shorter than 5 characters.
c.Requires().IsShorterThan();
// When c equals null, it doesn't contain
// any elements, so we'd expect
// the next lines to pass.
c.Requires().DoesNotContain("value");
c.Requires().DoesNotContainAny(new string[] { "a", "b" });

当空引用不是有效值时,开发人员可以使用 IsNotNull()方法。即使空引用是无效状态,也不需要这样做。以下示例显示了一些示例:

 // Use IsNotNull() to check for null.
c.Requires().IsNotNull().IsShorterThan();
// IsNotEmpty() will throw an ArgumentNullException
// when c equals null and an ArgumentException
// when c is empty but not null.
c.Requires().IsNotEmpty();

当该值列表不包含任何元素时,被检查的集合被视为包含所有指定的值。

当指定的值列表为空时,对ContainsAll方法的调用将始终成功。

以下示例显示了此行为:

 Collection<int> c =
new Collection<int> { , , , , };
// All checks will pass.
c.Requires().ContainsAll(new int[] { , });
c.Requires().ContainsAll(new int[] { });
c.Requires().ContainsAll(new int[] { });
c.Requires().ContainsAll(null);

当该集合不包含任何元素时,被检查的集合被认为不包含任何指定的值。

当指定的值列表为空时,对ContainsAny的调用将始终失败。

以下示例显示了此行为:

 Collection<int> c = new Collection<int> { , , , ,  };
// Next two checks will pass.
c.Requires().ContainsAny(new int[] { , });
c.Requires().ContainsAny(new int[] { });
// Next two checks will fail, because the
// specified lists are empty.
c.Requires().ContainsAny(new int[] { });
c.Requires().ContainsAny(null);

空字符串被认为具有0个字符的长度。

这里的理由与前面集合的描述大致相同。

以下示例显示了此行为:

 string s = null;
// Next check passes.
s.Requires().HasLength();
s.Requires().IsShorterThan();
s.Requires().IsLongerThan(-);
// You should use IsEmpty() or IsNotNull()
// if null is not a valid value.
s.Requires().IsEmpty();
s.Requires().IsNotNull().HasLength();

空字符串和空字符串不被视为相等,并且有多种方法需要检查。

以下示例显示了此行为:

 string s = null;
// The following checks will fail.
s.Requires().IsEqualTo(String.Empty);
s.Requires().IsEmpty();
// The following checks will pass.
s.Requires().IsNullOrEmpty();
s.Requires().IsNull();

一个 null 字符串 仅仅会包含 null 字符串 。这种情况适合使用 StartsWith,EndsWith 和 Contains 等方法。

这就是.NET 的 String 方法的工作原理。以不同方式实现它不仅非常困难,而且用户不会期望这种行为。

以下示例显示了此行为:

 string s = null;
// All checks below will fail.
s.Requires().Contains(String.Empty);
String.Empty.Requires().Contains(null);
String.Empty.Requires().EndsWith(null);

检查空引用是否属于某种类型将始终成功。

这对于反向操作也是有效的。

警告:

设计的行为已更改。与下面的文本描述的相反,在最终版本中,使用null参数调用IsOfType将始终失败,并且使用null参数调用IsNotOfType现在将始终成功。

下面的代码是违反直觉的,最终的 API 模仿了C# 'is' 运算符的行为。

在这里,我们看到了与之前看到的集合和字符串相同的问题。用户可以确定空引用有效。

如果没有,他将不得不在他的代码中添加一个IsNotNull检查。以下示例显示了此行为:

 object o = null;
// Both checks below will pass, because o equals null.
o.Requires().IsOfType(typeof(string));
o.Requires().IsNotOfType(typeof(object));
// Use IsNotNull() when null is not a valid value.
o.Requires().IsNotNull().IsOfType(typeof(string));
o.Requires().IsNotNull().IsNotOfType(typeof(object));

在具体翻之前觉得验证似乎是一件很简单的事情,但是这篇文档最后这几条体现出了这一方面的水深。

该文档与现在的 API 差距还是有一些的。而且该类库也无人维护了。原本我是打算用这个类库,或者是参考一下实现做些改进。

现在我觉得,我还是看看其他的类库吧。如果有时间的话就自己去实现一套。(估计没有的啦~

C# CuttingEdge.Conditions 验证帮助类库 文档翻译的更多相关文章

  1. C&num; 中参数验证方式的演变

    一般在写方法的时候,第一步就是进行参数验证,这也体现了编码者的细心和缜密,但是在很多时候这个过程很枯燥和乏味,比如在拿到一个API设计文档的时候,通常会规定类型参数是否允许为空,如果是字符可能有长度限 ...

  2. C&num; 中参数验证方式

    C# 中参数验证方式 一般在写方法的时候,第一步就是进行参数验证,这也体现了编码者的细心和缜密,但是在很多时候这个过程很枯燥和乏味,比如在拿到一个API设计文档的时候,通常会规定类型参数是否允许为空, ...

  3. 一些&period;NET 项目中经常使用的类库

    Web自己主动化測试   Watin Selenium  Selenium git .net 集合类扩展实现C5 Subscriber/Publisher 模式 Rx Nats 防御式编程 断言库 流 ...

  4. &period;NET业务实体类验证组件Fluent Validation

    认识Fluent Vaidation. 看到NopCommerce项目中用到这个组建是如此的简单,将数据验证从业务实体类中分离出来,真是一个天才的想法,后来才知道这个东西是一个开源的轻量级验证组建. ...

  5. C&num; 基于正则表达式的字符串验证

    输入的字符串校验,是开发中经常遇到的问题,常用的办法是利用正则表达式进行判断.其特点是简洁有效. 1.正则表达基础知识 正则表达式的教程很多,这里两个基础教程: a.http://www.cnblog ...

  6. 基于&period;NET平台常用的框架整理&lpar;转&rpar;

    自从学习.NET以来,优雅的编程风格,极度简单的可扩展性,足够强大开发工具,极小的 学习曲线,让我对这个平台产生了浓厚的兴趣,在工作和学习中也积累了一些开源的组件,就目前想到的先整理于此,如果再想到, ...

  7. Index

    我主要在研究.NET/C# 实现 PC IMERP 和 Android IMERP ,目的在解决企业通信中遇到的各类自动化问题   分布式缓存框架: Microsoft Velocity:微软自家分布 ...

  8. 【转】基于&period;NET平台常用的框架整理

    自从学习.NET以来,优雅的编程风格,极度简单的可扩展性,足够强大开发工具,极小的学习曲线,让我对这个平台产生了浓厚的兴趣,在工作和学习中也积累 了一些开源的组件,就目前想到的先整理于此,如果再想到, ...

  9. 基于&period;NET平台常用的框架整理

    自从学习.NET以来,优雅的编程风格,极度简单的可扩展性,足够强大开发工具,极小的学习曲线,让我对这个平台产生了浓厚的兴趣,在工作和学习中也积累了一些开源的组件,就目前想到的先整理于此,如果再想到,就 ...

随机推荐

  1. 基于jquery fly插件实现加入购物车抛物线动画效果&comma;jquery&period;fly&period;js

    在购物网站中,加入购物车的功能是必须的功能,有的网站在用户点击加入购物车按钮时,就会出现该商品从点击出以抛物线的动画相似加入购物车,这个功能看起来非常炫,对用户体验也有一定的提高.下面介绍基于jque ...

  2. c&plus;&plus; 离散数学 群的相关判断及求解

    采用C/C++/其它语言编程,构造一个n阶群<G={a,b,c,…},*>,G的阶|G|满足:3<=|G|<=6 1.判断该群是否是循环群,若是,输出该群的某个生成元. 2.给 ...

  3. 寒冰王座&lpar;DGA最长路&sol;完全背包&rpar;

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission( ...

  4. 测试题1 IOS面试基础题

    免责声明:答案来自本人,错误之处敬请谅解 1.用变量a写出以下定义 a.一个整型数    int a=5; b.一个指向整型数的指针  int *a; c.一个指向指针的指针,它指向的指针是指向一个整 ...

  5. c&plus;&plus;中调用cygwin&sol;x使用ncl

    1.C++中调用程序:   ShellExecute(NULL,L"open",L"cmd.exe",L"/c d: & cd cygwin ...

  6. 了解javascript中的this --实例篇

    对javascript this的赋值有了深一层的理解后,看一下比较复杂的情况,this的应用篇参考<对javascript this的理解>. #demo1 var name=&quot ...

  7. JAVA CAS单点登录&lpar;SSO&rpar;

    一.教程前言 教程目的:从头到尾细细道来单点登录服务器及客户端应用的每个步骤 单点登录(SSO):请看百科解释猛击这里打开 本教程使用的SSO服务器是Yelu大学研发的CAS(Central Auth ...

  8. IOS7修改Navigation Bar上的返回按钮文本颜色&comma;箭头颜色以及导航栏按钮的颜色

    解决方法 1: 自从IOS7后UINavigationBar的一些属性的行为发生了变化.你可以在下图看到: 现在,如果你要修改它们的颜色,用下面的代码: 1 2 3 4 self.navigation ...

  9. JavaScript(2)——对象属性、原型与原型链

    对象属性.原型与原型链 哈哈哈,我的第二篇博客哟,说的是对象属性.原型与原型链.可能这些只是某些小点串联起来的,逻辑性没有很强.所以会对文章的可读性和理解性带来一些困扰.不过,今天我又前进了那么一小步 ...

  10. 存储可靠性技术之 --RAID

    云计算项目交付时,不可避免的需要考虑存储磁盘采用何种RAID.例如:我们的项目工程师可能会建议大家连接克隆虚拟机系统盘组RAID 10,完整复制虚拟机数据盘 使用RAID5或者RAID6等,那么RAI ...