6

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

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.

我在这里想念什么?

更新

此类需要可以采用任何类型参数,包括structs 和内置类型,因此在我的情况下,泛型约束不是合适的解决方案。

4

6 回答 6

3

Select(x => (MySpecialModel)x)

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

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

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

于 2013-03-11T21:50:03.783 回答
3

尝试以下

select (T)(object)m;

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

要解决此问题,您需要object充当中间人。编译器了解如何转换MySpecialModelobject和从objectto T

于 2013-03-11T22:03:34.493 回答
1

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

select (T)(object)m;

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

于 2013-03-11T22:03:00.470 回答
1

如果你知道泛型类型总是一个类,你可以在你的类上添加一个类型约束:

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

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

.Select(x => (T)(object)x); 
于 2013-03-11T21:53:40.983 回答
0

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

void MyMethod<T>(T item) where T : class
{
    //...
}
于 2013-03-11T21:52:30.517 回答
0

您可能会应用Nullable<T>约束 - 这应该能够进行投射(至少使用“as”)。

于 2013-03-11T22:01:00.513 回答