C#类构造函数前置条件

时间:2015-11-13 10:12:51

标签: c# ninject ioc-container

假设我有一个带有构造函数的类

public RepresentativeService(IUserContext userContext, INavServiceClient navServiceClient)
{
    _userContext = userContext;
    _navServiceClient = navServiceClient;
}

我想添加前置条件

if (userContext == null) throw new ArgumentNullException(nameof(userContext));
if (navServiceClient == null) throw new ArgumentNullException(nameof(navServiceClient));
if (string.IsNullOrEmpty(userContext.CustomerNumber)) throw new ArgumentNullException(nameof(userContext.CustomerNumber));
if (string.IsNullOrEmpty(userContext.PersonalCode)) throw new ArgumentNullException(nameof(userContext.PersonalCode));

确保服务方法正常运行。此外,向方法添加条件是没有意义的,例如

public void Appoint(PrivatePerson person)

不应该检查userContext是否为null或者它需要的某个值是String.Empty。向构造函数添加前置条件可能是不好的决定,但另一方面,它会快速失败并给出正确的精确错误。

服务只是网络服务的一个外观,来自第三方。在没有先决条件的情况下,我允许用户说出类似:“删除具有空id的代表”,这似乎不正确。如果我不添加它们,那么为它们添加单元/集成测试也没有意义。

我的问题是:我应该添加先决条件吗?

2 个答案:

答案 0 :(得分:3)

在构造函数中添加前置条件通常是个好主意。你正在保护你的不变量。你不希望有人用糟糕的状态来实例化你的类。例如,当它不能为null时,某些东西为null。您还应该为在方法上下文中有意义的方法添加前置条件。在您的示例中,检查person是否为null。

public void Appoint(PrivatePerson person)
{
   if (person == null)
   {
       throw new ArgumentNullException(nameof(person));
   }

   // code
}

这也称为防御性编程。您假设代码的调用者正在传递您不寻常的参数,因此您需要检查代码中的无效参数。这是非常好的做法。

关于测试,您应该在单元测试中测试您的前提条件。您的前提条件是代码执行流程的一部分。如果你不在测试中运用它们,你就无法确定它们是否按预期工作。

总之,在任何地方添加前提条件,以保证通常无处不在的正确对象状态。

答案 1 :(得分:2)

在构造函数中添加前置条件实际上很常见。哎呀,它甚至被我们使用和喜爱的CLR课程所使用! 例如,如果您反编译System.Linq.OrderedEnumerable,您将找到此构造函数:

internal OrderedEnumerable(IEnumerable<TElement> source, Func<TElement, TKey> keySelector, IComparer<TKey> comparer, bool descending)
{
  if (source == null)
    throw Error.ArgumentNull("source");
  if (keySelector == null)
    throw Error.ArgumentNull("keySelector");
  this.source = source;
  this.parent = (OrderedEnumerable<TElement>) null;
  this.keySelector = keySelector;
  this.comparer = comparer != null ? comparer : (IComparer<TKey>) Comparer<TKey>.Default;
  this.descending = descending;
}