gpt4 book ai didi

c# - 为什么这个 Queryable.Where 调用会改变 Queryable 的类型参数?

转载 作者:太空宇宙 更新时间:2023-11-03 10:39:55 24 4
gpt4 key购买 nike

重现问题的代码

我遇到了一个 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,该 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() );

不幸的是,输出保持不变。

最佳答案

Why does this happen?

因为 Where调用正在接收 ParentQueryElement 的类型参数作为TSource .它根据 TSource 创建结果作为一个新对象......所以你最终得到了一些“知道”关于 ParentQueryElement 的东西而不是 ChildQueryElement .

无需使用 LINQ 即可轻松演示这一点:

using System;

public interface IWrapper<out T>
{
T Value { get; }
}

public class Wrapper<T> : IWrapper<T>
{
private readonly T value;

public Wrapper(T value)
{
this.value = value;
}

public T Value { get { return value; } }
}

class Program
{
static void Main(string[] args)
{
IWrapper<string> original = new Wrapper<string>("foo");
IWrapper<object> original2 = original;
IWrapper<object> rewrapped = Rewrap(original2);

Console.WriteLine(original2.GetType()); // Wrapper<string>
Console.WriteLine(rewrapped.GetType()); // Wrapper<object>
}

static IWrapper<T> Rewrap<T>(IWrapper<T> wrapper)
{
return new Wrapper<T>(wrapper.Value);
}
}

Is there any way I can avoid this, other than skipping the step where it gets stored in an object?

好吧,你可以调用Where动态地,此时将在执行时推断类型参数:

dynamic copy = ...;

Expression<Func<ChildQueryElement, bool>> filter = qe1 => true;
// Can't call an extension method "on" dynamic; call it statically instead
copy = Queryable.Where(copy, filter);

注意表达式树类型需要是Func<ChildQueryElement, bool>还有……我不清楚这对您来说是否是个问题。

关于c# - 为什么这个 Queryable.Where 调用会改变 Queryable 的类型参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25778701/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com