【发布时间】:2017-04-14 21:47:34
【问题描述】:
我有一个通用的 QueryProvider,可以根据请求提供某种类型的数据(序列)。
在提供数据之前,我检查请求的类型 IsAssignableFrom 是否是我可以提供的类型。
编译器对从IEnumerable<TData> 转换为IEnumerable<TResult> 感到满意,但它抱怨将TData 转换为TResult
class MyClass<TData>
{
private IEnumerable<TData> GetSequence() {return Enumerable.Empty<TData>(); }
private TData GetSingleValue() { return default(TData); }
public TResult GetResult<TResult>()
{
TResult result;
if (typeof(TResult).IsAssignableFrom(typeof(IEnumerable<TData>)))
{ // we can Assign IEnumerable<TData> to TResult
IEnumerable<TData> data = GetSequence();
result = (TResult)data;
}
else if (typeof(TResult).IsAssignableFrom(typeof(TData)))
{ // we can assing TData to TResult:
TData data = GetSingleValue();
result = (TResult)data;
编译器错误 CS0030 无法将类型“TData”转换为“TResult”
}
else
throw new InvalidOperationException("Can't provide data");
return result;
}
}
为什么转换为IEnumerable<TData> 为IEnumerable<TResult> 没有问题,但不能将TData 转换为TResult?
在一些 cmets 之后添加
事实上,IsAssignable 与问题无关。让我困惑的是IEnumerable转换没有问题,而直接转换是。
where 子句当然解决了这个问题,但这只是改变了一个问题:为什么IEnumerable 可以不使用 where 子句,为什么直接转换需要 where 子句:
IEnumerable<TData> items = ...;
TData item = items.FirstOrDefault();
TResult result1 = (IEnumerable<TResult>) items; // compile time OK
TResult result2 = (TResult) item; // compile time problem
var obj = (object)item;
TResult result3 = (TResult) obj; // compile time OK;
如果编译器不能证明我的显式转换是不正确的,那它会给我带来怀疑的好处吗?
【问题讨论】:
-
可能是从单例 (typeof(TData)) 转换为 IEnumerable 的问题。
-
通用代码必须是类型安全的,编译器不能保证强制转换总是可行的。运行时测试不足以说服它。您必须自己提供该保证并添加约束
where TResult : TData。