C# 将对象转换为 IEnumerable<T> 并在可枚举类型为值类型时使用 Linq 扩展,例如枚举

C# Cast object to IEnumerable<T> and use Linq extensions when the enumerable type is a value type e.g. enum

如果我知道类型是可枚举类型?

IEnumerable<object> 的简单转换适用于引用类型,但不适用于枚举等值类型,例如:

StringComparison[] myArray = new StringComparison[] {
  StringComparison.OrdinalIgnoreCase,
  StringComparison.Ordinal
};

void DoSomething(object value) {
    IEnumerable<object> items = (IEnumerable<object>) value;

    // use the enumerable with System.Linq.Enumerable extensions
    // items.Count()
    // items.Distinct()
}

DoSomething(myArray);

对于枚举,演员会抛出 System.InvalidCastException: 'Unable to cast object of type 'System.StringComparison[]' to type 'System.Collections.Generic.IEnumerable^1[System.Object]'.'

其他答案(例如 C# object to array)建议通过强制转换为非泛型 IEnumerable 来实现此目的。不幸的是,我想要的扩展方法(Count()Distinct())不适用于非泛型 IEnumerable.

背景:上面的例子是经过简化的。我的实际用例是我编写了一个自定义 System.ComponentModel.DataAnnotations.ValidationAttribute,它对可枚举属性 属性 执行检查。 ValidationAttribute 有一个方法 IsValid(object value),枚举值数组被传递给该方法,我需要迭代该方法 属性 但由于这个原因它失败了。

谢谢!

数组StringComparison[]实现了IEnumerableIEnumerable<StringComparison>接口,但没有实现IEnumerable<object>.

不幸的是,您不能使用泛型协变来将 IEnumerable<StringComparison> 类型的对象分配给 IEnumerable<object> 类型的变量,因为 StringComparison 不是引用类型: 你不能像访问对象一样访问 StringComparison[] 的任何旧元素,因为它没有正常的对象头等

可以做的是:

IEnumerable<object> items = ((IEnumerable)value).Cast<object>();

之所以有效,是因为数组实现了 IEnumerable,而 Enumerable.CastIEnumerable 上的扩展方法,而不是 IEnumerable<T>

每次从 items 获取元素时,Cast<object>() 方法都会参与并将 StringComparison 值类型装箱到一个对象中。

这意味着多次迭代列表(因为调用大量 linq 方法会这样做)可能很昂贵,因为它每次都会分配新对象。您可能希望将盒装对象缓存到列表中以避免此成本:

List<object> items = ((IEnumerable)value).Cast<object>().ToList();