在linq查询中转换泛型类型

本文关键字:转换 泛型类型 查询 linq | 更新日期: 2023-09-27 18:03:19

所以我有一个接受泛型类型参数的类,如果类型参数是给定类型的子类,则做一些特殊处理。

IEnumerable<T> models = ...
// Special handling of MySpecialModel
if (filterString != null && typeof(MySpecialModel).IsAssignableFrom(typeof(T)))
{
    var filters = filterString.Split(...);
    models = 
        from m in models.Cast<MySpecialModel>()
        where (from t in m.Tags
               from f in filters 
               where t.IndexOf(f, StringComparison.CurrentCultureIgnoreCase) >= 0
               select t)
              .Any()
        select (T)m;
}

但是在最后一行

出现了异常
Cannot convert type 'MySpecialModel' to 'T'

如果我改变代码使用as而不是强制转换,我得到这个错误。

The type parameter 'T' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint.

我在这里错过了什么?

这个类需要可以接受任何类型参数,包括struct和内置类型,所以在我的情况下,泛型约束不是一个合适的解决方案。

在linq查询中转换泛型类型

Do Select(x => (MySpecialModel)x)

LINQ Cast<T>方法只适用于将元素转换为元素已经存在的类型(例如基类型、派生类型或接口)。它不打算将能够被强制转换为目标类型的对象强制转换。(例如:new List<int>{1,2,3}.Cast<long>()也会抛出异常

上面的答案没有错,但它没有解决问题。

仅仅因为你用反射证明了泛型参数绑定到给定类型,并不意味着编译器知道它是。为了做到这一点,你需要将T实例转换为一个通用类型(例如object),然后将其转换为特定类型。例:(将查询的最后一行改为select (T)(object)m应该可以达到目的。

试试下面的

select (T)(object)m;

在运行时,您已经验证了TMySpecialModel的子类型,但编译器在编译时无法访问此信息。它只看到两个不相关的类型:TMySpecialModel之间的尝试转换。

要解决这个问题,你需要使用object作为中间人。编译器理解如何将MySpecialModel转换为object,以及如何从object转换为T

最直接的修复方法是先强制转换为object,然后再强制转换为T:

select (T)(object)m;

问题是您的检查发生在运行时,但编译器不知道T必须是if语句中MySpecialModel的实例。因此,它只是看到您试图从MySpecialModel转换为任意类型T,这是不安全的,因此出现错误。

如果您知道泛型类型将始终是一个类,则可以在类中添加类型约束:

public class Test<T> where T : class {}

否则,按照smartcaveman的建议,通过对象执行双强制转换:

.Select(x => (T)(object)x); 

要使用as关键字,请将class约束放在泛型参数上:

void MyMethod<T>(T item) where T : class
{
    //...
}

您可以应用Nullable<T>约束-它应该启用强制转换的可能性(至少使用"as")