.net 确定IEnumerable中是否有任何元素<;T>;可以转换为U型

.net 确定IEnumerable中是否有任何元素<;T>;可以转换为U型,.net,generics,equals,iequatable,.net,Generics,Equals,Iequatable,在较新版本的.net中,有许多扩展方法接受IEnumerable或IEnumerable。其中一种方法是of type,它返回一个只包含原始序列中可以转换为指定类型T的元素的可枚举项。该方法使用一个非泛型的IEnumerable来处理原始列表的所有项,而不管原始列表的类型和目标类型如何,都可以轻松地处理。如果原始列表是一个IEnumerable且TResult是int,则它会将原始列表中的每一项作为非通用对象来获取,然后将其强制转换,即使它可以只使用原始IEnumerator。如果原始列表是一个

在较新版本的.net中,有许多扩展方法接受
IEnumerable
IEnumerable
。其中一种方法是
of type
,它返回一个只包含原始序列中可以转换为指定类型
T
的元素的可枚举项。该方法使用一个非泛型的
IEnumerable
来处理原始列表的所有项,而不管原始列表的类型和目标类型如何,都可以轻松地处理。如果原始列表是一个
IEnumerable
TResult
int
,则它会将原始列表中的每一项作为非通用
对象来获取,然后将其强制转换,即使它可以只使用原始
IEnumerator
。如果原始列表是一个
IEnumerable
TResult
StringBuilder
,则它同样会将原始列表中的所有项目装箱,即使它们都不可能转换为目标类型

编写一个方法,将
IEnumerable
转换为
IEnumerable
,有效地处理原始枚举中的所有项都保证出现在后者中的情况,有多困难,或者,源枚举数的类型意味着后者为空(假设
IEnumerable
a类型支持的所有变体都返回相同的项序列)?可以肯定的是,人们不会经常尝试将序列强制转换为它已经拥有的类型,但是拥有一个具有多个类型参数的泛型类并不少见,这些类型参数有时会重叠,有时不会重叠。

IEquatable.Equals
应该与
对象一致。Equals
重写。我不确定直接使用
IEquatable
是否有任何价值

在我看来,您只是想确保已装箱的对象使用未装箱的值进行相等性测试。你应该能够用这样一个等式做你想做的大部分事情:

private static bool Equals<T1, T2>(T1 first, T2 second)
{
    // if either are boxed, try Equals with unboxed values (dynamic)
    if (typeof(T2) == typeof(object) && second.GetType().IsValueType)
    {
        dynamic dsec = second;
        if (typeof(T1) == typeof(object) && second.GetType().IsValueType)
        {
            dynamic dfir = first;
            return Equals(dfir, dsec);
        }
        return Equals(first, dsec);
    }
    if (typeof(T1) == typeof(object) && second.GetType().IsValueType)
    {
        dynamic dfir = first;
        Equals<dynamic, T2>(dfir, second);
    }
    // neither are boxed, just fall back to their Equals overrides...
    var t = Object.Equals(first, second);
    return t;
}

结果为真、真、假。因此,很明显,两个对象可以“相等”,但不能相互转换。

值类型不能派生自基类。它们唯一的公共基始终是Object,这意味着如果要比较两种不同的值类型,而不特别使用知道要比较的两种类型的方法,则必须至少框选一个值,这两种类型都是可枚举的。所以不写不同的方法是不可能的。@PeterRitchie:我假设两个不同类型的项可以安全地假设为不相等。如果其中一个类型参数是值类型,并且不能转换为另一个类型,则项目无法匹配。如果一个是值类型,另一个不是,但前者可转换为后者并实现
IEquatable
,则可以通过检查第二个的类型是否与第一个匹配来执行比较,如果是,则强制转换第二个(已经是引用类型,因此没有装箱)到
IEquatable
并使用
IEquatable.Equals(firstObject)
。这将是一个不安全的假设,因为可以比较不同的类型——这也是Object.Equals可重写的部分原因。例如,您可以将以下结果设置为true:字节b=a;int i=1;var t=b.Equals(i)等式封装在Equals中,虽然两种类型可以相互转换,但可以进行比较,您不知道Equals是否起作用that@PeterRitchie:之所以
b.Equals(i)
不是因为
b
覆盖了
Object.Equals
,而是它重载等于数字类型的比较。将任一操作数强制转换为
对象
,比较结果将返回false;我不知道框架中有任何类型会重载
对象。Equals
,在将自身与任何其他类型的对象进行比较时返回
true
,值类型和可空类型除外,它们将与装箱的等价对象进行比较。如前所述,您的代码违反了这样的规则:类型应该以定义等价关系的方式重写
Equals(Object)
,这反过来意味着
Object.Equals(X,Y)
应该等于
Object.Equals(Y,X)
对于任何
X
Y
。即使一个向struct
one
添加了代码,以覆盖其
Equals
方法,使其与
Two
的方法具有反射性,这种行为仍然与我所知道的框架中的任何行为都大不相同。您能识别任何情况下
Object.Equals(X,Y)
可以为真,即使
X.GetType()!=Y.GetType()
X
Y
都是框架类型?似乎与您最初的问题无关,因为许多框架类型也违反了准则。比如:
字节b=10;int32i=b;Assert(b.Equals(i)=(b==i))
显然,Byte没有正确定义由“在实现相等运算符(=)时重写相等方法并使它们执行相同的操作”定义的等价关系。在许多情况下,
==
运算符的重载将导致在进行比较之前将两侧转换为相同的类型。在您的示例中,
==
运算符的两个操作数都将转换为类型
Int32
。某些类型还定义了
等于
的重载,这也可能导致在比较之前进行转换。但是,请注意,操作泛型集合类型的方法基本上不会使用
=
等于的重载,而是使用提供的相等比较器,或者使用
IEquatable.Equa
public struct One
{
    public int Value;
    public override int GetHashCode()
    {
        return Value.GetHashCode();
    }
}

public struct Two
{
    public int Value;
    public override bool Equals(object obj)
    {
        if (obj is Two) return this == (Two)obj;
        if (obj is One) return this.Equals((One)obj);
        return base.Equals(obj);
    }

    public bool Equals(One one)
    {
        return one.Value == Value;
    }

    public static bool operator==(Two one, Two two)
    {
        return one.Value == two.Value;
    }

    public static bool operator !=(Two one, Two two)
    {
        return !(one == two);
    }
    public override int GetHashCode()
    {
        return Value.GetHashCode();
    }
}

// ...

    Console.WriteLine(two.Equals(one));
    Console.WriteLine(two.Equals((Object)one));
    Console.WriteLine(two.GetType().IsAssignableFrom(one.GetType()) || one.GetType().IsAssignableFrom(two.GetType()));