在linq查询中转换泛型类型

时间:2013-03-11 21:48:33

标签: c# .net linq generics casting

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

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和内置类型,因此在我的情况下,通用约束不适合作为解决方案。

6 个答案:

答案 0 :(得分:3)

执行Select(x => (MySpecialModel)x)

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

上述答案没错,但没有解决这个问题。

仅仅因为您已经通过反射证明泛型参数绑定到给定类型,并不意味着编译器知道它是。为了完成这项工作,您需要将T实例转换为通用类型(例如object),然后将其转换为特定类型。例如(将查询中的最后一行更改为select (T)(object)m应该可以解决问题。

答案 1 :(得分:3)

尝试以下

select (T)(object)m;

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

要解决此问题,您需要使用object作为中间人。编译器了解如何将MySpecialModel转换为object并从object转换为T

答案 2 :(得分:1)

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

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

否则,通过 smartcaveman 建议:

,通过对象执行双重投射
.Select(x => (T)(object)x); 

答案 3 :(得分:1)

最直接的解决方法是在投射到object之前先转换为T

select (T)(object)m;

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

答案 4 :(得分:0)

要使用as关键字,请在您的通用参数上添加class约束:

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

答案 5 :(得分:0)

您可以应用Nullable<T>约束 - 这应该允许投射(至少使用“as”)。