所以我有一个接受泛型类型参数的类,如果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
和内置类型,因此在我的情况下,通用约束不适合作为解决方案。
答案 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;
在运行时,您已验证T
是MySpecialModel
的子类型,但编译器在编译时无法访问此信息。它只是看到两种不相关类型之间的尝试转换:T
和MySpecialModel
。
要解决此问题,您需要使用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”)。