如何指定表示"collection of nullable types"的约束?



我正在尝试创建一个通用的参数验证方法,该方法检查集合参数是否为空、空或包含空元素

public void Foo(ICollection<MyType> bar)
{
    // Validate parameters
    ThrowIfNullEmptyOrContainsNull(bar, "bar");
                  .
                  .
                  .

如果我只在类型约束中指定ICollection<T>,则if (value.Contains(null))生成错误,因为T可能不是可为 null 的类型。

这就是我想出的,但似乎不对:

internal static T1 ThrowIfNullEmptyOrContainsNull<T1, T2>(T1 value, string name)
    where T1 : ICollection<T2>
    where T2 : class
{
    if (ReferenceEquals(value, null))
        throw new ArgumentNullException(name);
    if (value.Count == 0)
        throw new ArgumentException("Empty collection not allowed", name);
    if (value.Contains(null))
        throw new ArgumentException("Collection contains one or more null elements", name);
    return value;
}

。但是我必须使用显式参数类型调用该方法,如下所示:

public void Foo(ICollection<MyType> bar)
{
    // Validate parameters
    ThrowIfNullEmptyOrContainsNull<(ICollection<MyType>, MyType>(bar, "bar");
                  .
                  .
                  .

如果没有在调用中明确指定 T1 和 T2,我会收到错误"类型参数......不能从用法中推断出来"。

谁能阐明如何做到这一点?

只是不要使用Contains . 循环访问集合并将值与显式null进行比较:

internal static T1 ThrowIfNullEmptyOrContainsNull<T1, T2>(T1 value, string name)
    where T1 : ICollection<T2>
{
    if (ReferenceEquals(value, null))
        throw new ArgumentNullException(name);
    if (value.Count == 0)
        throw new ArgumentException("Empty collection not allowed", name);
    foreach (var item in value)
        if (item == null)
            throw new ArgumentException("Collection contains one or more null elements", name);
    return value;
}

像这样的事情呢:

if (value.Any(item => item == null))
{
    throw new ArgumentException("Collection contains one or more null elements", name);
}

如:

internal static T1 ThrowIfNullEmptyOrContainsNull<T1, T2>(T1 value, string name)
    where T1 : ICollection<T2>
{
    if (ReferenceEquals(value, null)) 
        throw new ArgumentNullException(name);
    if (value.Count == 0) 
        throw new ArgumentException("Empty collection not allowed", name);
    if (value.Any(item => item == null)) 
        throw new ArgumentException("Collection contains 1 or more null items", name);
    return value;
}

我们可以将不可为空的类型与 null 进行比较,因为所有对象都可以转换为 object,因此可以进行比较:

bool obviouslyFalse = 1 == null;

这将导致警告,但有效。显然,它将始终是false的,实际上编译器将通过删除比较并向我们提供等效项来优化,就好像我们有bool obviouslyFalse = false;一样。

对于泛型,这同样适用于:

T item = getTFromSomewhere;
bool obviouslyFalseIfTIsntNullable = item == null;

那么这对所有可能的T都有效,虽然编译器不能删除比较,但抖动可以,而且确实会。

因此,我们可以拥有:

internal static TCol ThrowIfNullEmptyOrContainsNull<TCol, TEl>(TCol collection, string name)
    where TCol : ICollection<TEl>
{
  if (ReferenceEquals(value, null))
      throw new ArgumentNullException(name);
  if (value.Count == 0)
    throw new ArgumentException("Empty collection not allowed", name);
  foreach(var item in collection)
    if(item == null)
      throw new ArgumentException("Collection cannot contain null elements", name);
  return value;
}

这将起作用,但如果我们有一大堆不可为空的类型,那就浪费了;抖动不太可能删除该迭代,即使它什么都不做,所以它仍然会得到一个枚举器并调用MoveNext(),直到它返回 false。我们可以提供帮助:

internal static TCol ThrowIfNullEmptyOrContainsNull<TCol, TEl>(TCol collection, string name)
    where TCol : ICollection<TEl>
{
  if (ReferenceEquals(value, null))
      throw new ArgumentNullException(name);
  if (value.Count == 0)
    throw new ArgumentException("Empty collection not allowed", name);
  if(default(TEl) == null)
    foreach(var item in collection)
      if(item == null)
        throw new ArgumentException("Collection cannot contain null elements", name);
  return value;
}

由于 default(TEl) == null 对于可为 null 的类型(包括 Nullable<T>(始终为 true,而对于不可为空的类型始终为 false,因此抖动将通过切断所有类型的此比较并删除不可为空类型的整个枚举来优化。因此,该方法将立即确定大量整数数组(例如(。

扩展方法呢? 根据需要使用例外或消息进行包装

  public static bool IsNullOrEmpty<T>(this ICollection<T> alist) where T:class
    {
        if (alist == null || alist.Count == 0){
            return true;
        }
        if (alist.Any(t => t == null)) {
            return true;
        }
        return false;
    }

用:

 if ( myList.IsNullOrEmpty  ) {
   //.. exception, error handling
  }

无需传递 MyList 的类型,因为 MyList 必须实现 ICollection可能对您有用,无需传递类型要求。
在建议中添加了 T:类。 但是你已经知道了:-(

最新更新