【发布时间】:2014-11-04 21:16:23
【问题描述】:
重现问题的代码
我遇到了IQueryable.Where<TSource> 调用返回IQueryable<TOther> 而TOther != TSource 的情况。我整理了一些示例代码来重现它:
using System;
using System.Collections.Generic;
using System.Linq;
namespace IQueryableWhereTypeChange {
class Program {
static void Main( string[] args ) {
var ints = new List<ChildQueryElement>();
for( int i = 0; i < 10; i++ ) {
ints.Add( new ChildQueryElement() { Num = i, Value = i.ToString() } );
}
IQueryable<ChildQueryElement> theIQ = ints.AsQueryable();
Object theObj = theIQ;
Type theObjElementType = ( (IQueryable<ParentQueryElement>) theObj ).ElementType;
Type theObjGenericType = ( (IQueryable<ParentQueryElement>) theObj ).GetType().GetGenericArguments()[ 0 ];
var iQ = ( (IQueryable<ParentQueryElement>) theObj );
var copy = iQ;
Type copyType1 = copy.GetType().GetGenericArguments()[ 0 ];
Type elementType1 = copy.ElementType;
copy = copy.Where( qe1 => true );
Type copyType2 = copy.GetType().GetGenericArguments()[ 0 ];
Type elementType2 = copy.ElementType;
Console.WriteLine( "theObjElementType : " + theObjElementType.ToString() );
Console.WriteLine( "theObjGenericType : " + theObjGenericType.ToString() );
Console.WriteLine( "copyType1 : " + copyType1.ToString() );
Console.WriteLine( "elementType1 : " + elementType1.ToString() );
Console.WriteLine( "copyType2 : " + copyType2.ToString() );
Console.WriteLine( "elementType2 : " + elementType2.ToString() );
}
}
public class ParentQueryElement {
public int Num { get; set; }
}
public class ChildQueryElement : ParentQueryElement {
public string Value { get; set; }
}
}
这个程序的输出是:
theObjElementType : IQueryableWhereTypeChange.ChildQueryElement
theObjGenericType : IQueryableWhereTypeChange.ChildQueryElement
copyType1 : IQueryableWhereTypeChange.ChildQueryElement
elementType1 : IQueryableWhereTypeChange.ChildQueryElement
copyType2 : IQueryableWhereTypeChange.ParentQueryElement
elementType2 : IQueryableWhereTypeChange.ParentQueryElement
代码结果总结
因此,我们将IQueryable<ChildQueryElement> 存储在Object 中,然后将对象转换为IQueryable<ParentQueryElement>,其中子类型继承自父类型。此时存储在Object 变量中的对象仍然知道它是子类型的集合。然后我们在它上面调用Queryable.Where,但是返回的对象不再知道它包含子类型,并且认为它只包含父类型。
问题
为什么会这样?除了跳过将其存储在对象中的步骤之外,有什么方法可以避免这种情况?我问这个是因为我正在处理一个第三方 API,它要求我传递一个 Object,而且我不想重写一堆第三方代码。
更新示例代码
在得到 Jon Skeet 的一些建议后,我尝试了这个示例代码,它使用动态变量进行复制。将Main 的正文替换为以下内容:
var ints = new List<ChildQueryElement>();
for( int i = 0; i < 10; i++ ) {
ints.Add( new ChildQueryElement() { Num = i, Value = i.ToString() } );
}
IQueryable<ChildQueryElement> theIQ = ints.AsQueryable();
Object theObj = theIQ;
Type theObjElementType = ( (IQueryable<ParentQueryElement>) theObj ).ElementType;
Type theObjGenericType = ( (IQueryable<ParentQueryElement>) theObj ).GetType().GetGenericArguments()[ 0 ];
var iQ = ( (IQueryable<ParentQueryElement>) theObj );
dynamic copy = iQ;
Type copyType1 = copy.GetType().GetGenericArguments()[ 0 ];
Type elementType1 = ((IQueryable)copy).ElementType;
Expression<Func<ParentQueryElement, bool>> del = qe => true;
copy = Queryable.Where( copy, del );
Type copyType2 = copy.GetType().GetGenericArguments()[ 0 ];
Type elementType2 = ((IQueryable)copy).ElementType;
Console.WriteLine( "theObjElementType : " + theObjElementType.ToString() );
Console.WriteLine( "theObjGenericType : " + theObjGenericType.ToString() );
Console.WriteLine( "copyType1 : " + copyType1.ToString() );
Console.WriteLine( "elementType1 : " + elementType1.ToString() );
Console.WriteLine( "copyType2 : " + copyType2.ToString() );
Console.WriteLine( "elementType2 : " + elementType2.ToString() );
不幸的是,输出保持不变。
【问题讨论】:
-
我猜这对新用户来说有点敏感。将非代码突出显示为代码是愚蠢的。在这里,进行编辑。
-
@Will 谢谢你。你是怎么做到的?它只是没有给你警告吗?因为如果是这样,那么这就回答了我的元问题。
-
不,它不会在某个代表阈值之后进行检查。不知道那是什么。或者,您可以关闭所选代码块的语法突出显示(搜索 Meta Stack Exchange 了解详细信息),或者将其标记为代码并在您的 q/a 下方留下评论以寻求帮助:/
标签: c# linq types iqueryable