如何指定一个表示“可空类型集合”的约束?

时间:2014-12-02 20:25:09

标签: c# .net nullable generic-collections type-constraints

我试图创建一个通用的参数验证方法来检查null,empty或的集合参数是否包含null元素

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

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

这就是我想出来的,但它似乎并不正确:

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,我会收到错误&#34;类型参数...无法从使用情况中推断出来。&#34;。

有人能说清楚如何做到这一点吗?

4 个答案:

答案 0 :(得分:2)

不要使用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;
}

答案 1 :(得分:2)

如下:

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;
}

答案 2 :(得分:1)

扩展方法怎么样?根据需要包含异常或消息

  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:Class to suggestion。但你已经知道了: - )

答案 3 :(得分:1)

我们可以将非可空类型与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对于可空类型(包括Nullable<T>)始终为true,对于非可空类型始终为false,因此将通过删除所有类型的比较来优化抖动,并删除整个枚举对于非可空类型。因此,立即可以通过该方法获得大量的整数(例如)。