Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 当编译时类型未知时,将对象动态强制转换为类型_C#_Linq_Casting_Expression - Fatal编程技术网

C# 当编译时类型未知时,将对象动态强制转换为类型

C# 当编译时类型未知时,将对象动态强制转换为类型,c#,linq,casting,expression,C#,Linq,Casting,Expression,我有一种从数据库获取数据的方法,它是通用的: public static IQueryable<T> GetData<T>(IQueryable<T> data, some more parameters) 在这一点上,o确实是IQueryable(IQueryable),因此应该能够作为GetData的参数。但是,我对它只有“对象”的引用,当然,不能这样使用它 所以,我的问题是:有没有办法在o正好是“IQueryable”时动态地将“o”强制转换为“IQu

我有一种从数据库获取数据的方法,它是通用的:

public static IQueryable<T> GetData<T>(IQueryable<T> data, some more parameters)
在这一点上,o确实是IQueryable(IQueryable),因此应该能够作为GetData的参数。但是,我对它只有“对象”的引用,当然,不能这样使用它


所以,我的问题是:有没有办法在o正好是“IQueryable”时动态地将“o”强制转换为“IQueryable”。我知道cast是编译时的东西,但我希望有人有某种解决方法。或者可能我尝试得太多了。

您可以利用
动态
功能。对于已经需要进行反射的情况,Dynamic非常适合:

dynamic o = Expressin.Lambda(...

看看这个家伙:

首先。我们假定围绕您的GetData方法的类称为Foo

public static class Foo {

    public static IQueryable<T> GetData<T>(IQueryable<T> data, int bar, bool bravo) {
        // ... whatever
    }
公共静态类Foo{
公共静态IQueryable GetData(IQueryable数据、int-bar、bool-bravo){
//…随便
}
然后,我们尝试反思GetData<>的MethodInfo(我指的是实际的模板、通用定义,而不是它的封闭具体化)。我们尝试在Foo类诞生时获得(并成功)

    private static readonly MethodInfo genericDefinitionOf_getData;

    static Foo() {
        Type prototypeQueryable = typeof(IQueryable<int>); 
        // this could be any IQuerable< something >
        // just had to choose one

        MethodInfo getData_ForInts = typeof(Foo).GetMethod(
            name: "GetData", 
            bindingAttr: BindingFlags.Static | BindingFlags.Public,
            binder: Type.DefaultBinder,
            types: new [] { prototypeQueryable, typeof(int), typeof(bool) },
            modifiers: null
        );
        // now we have the GetData<int>(IQueryable<int> data, int bar, bool bravo)
        // reffered by the reflection object getData_ForInts

        MethodInfo definition = getData_ForInts.GetGenericMethodDefinition();
        // now we have the generic (non-invokable) GetData<>(IQueryable<> data, int bar, bool bravo)
        // reffered by the reflection object definition

        Foo.genericDefinitionOf_getData = definition;
        // and we store it for future use
    }
private static readonly MethodInfo_getData的泛型定义;
静态Foo(){
Type prototypeQueryable=typeof(IQueryable);
//这可能是任何可解决的
//我只能选一个
MethodInfo getData\u ForInts=typeof(Foo).GetMethod(
名称:“GetData”,
bindingAttr:BindingFlags.Static | BindingFlags.Public,
活页夹:Type.DefaultBinder,
类型:new[]{prototypeQueryable,typeof(int),typeof(bool)},
修饰符:null
);
//现在我们有了GetData(IQueryable数据、int-bar、bool-bravo)
//由反射对象getData\u ForInts引用
MethodInfo定义=getData_ForInts.GetGenericMethodDefinition();
//现在我们有了通用的(不可调用的)GetData(IQueryable数据、int-bar、bool-bravo)
//由反射对象定义参照
Foo.genericDefinitionOf_getData=定义;
//我们储存起来以备将来使用
}
然后,我们编写一个非泛型的方法变体,该变体应针对作为参数发送的实际元素类型调用特定的泛型方法:

    public static IQueryable GetDataEx(IQueryable data, int bar, bool bravo) {
        if (null == data)
            throw new ArgumentNullException("data");
        // we can't honor null data parameters

        Type typeof_data = data.GetType(); // get the type (a class) of the data object
        Type[] interfaces = typeof.GetInterfaces(); // list it's interfaces

        var ifaceQuery = interfaces.Where(iface => 
            iface.IsGenericType && 
            (iface.GetGenericTypeDefinition() == typeof(IQueryable<>))
        ); // filter the list down to just those IQueryable<T1>, IQueryable<T2>, etc interfaces
        Type foundIface = ifaceQuery.SingleOrDefault();
        // hope there is at least one, and only one

        if (null == foundIface) // if we find more it's obviously not the time and place to make assumptions
            throw new ArgumentException("The argument is ambiguous. It either implements 0 or more (distinct) IQueryable<T> particularizations.");

        Type elementType = foundIface.GetGenericArguments()[0];
        // we take the typeof(T) out of the typeof(IQueryable<T>)

        MethodInfo getData_particularizedFor_ElementType = Foo.genericDefinitionOf_getData.MakeGenericMethod(elementType);
        // and ask the TypeSystem to make us (or find us) the specific particularization
        // of the **GetData < T >** method

        try {
          object result = getData_particularizedFor_ElementType.Invoke(
              obj: null,
              parameters: new object[] { data, bar, bravo }
          );
          // then we invoke it (via reflection)

          // and obliviously "as-cast" the result to IQueryable
          // (it's surely going to be ok, even if it's null)
          return result as IQueryable;

        } catch (TargetInvocationException ex) {
          // in case of any mis-haps we make pretend we weren't here
          // doing any of this
          throw ex.InnerException;

          // rethink-edit: Actually by rethrowing this in this manner
          // you are overwriting the ex.InnerException's original StackTrace
          // so, you would have to choose what you want: in most cases it's best not to rethrow
          // especially when you want to change that which is being thrown
        }
    }


}
publicstatic IQueryable GetDataEx(IQueryable数据,int-bar,bool-bravo){
if(null==数据)
抛出新的异常(“数据”);
//我们不能接受空数据参数
Type typeof_data=data.GetType();//获取数据对象的类型(类)
Type[]interfaces=typeof.GetInterfaces();//列出它的接口
var ifaceQuery=interfaces.Where(iface=>
iface.IsGenericType&&
(iface.GetGenericTypeDefinition()==typeof(IQueryable))
);//将列表向下过滤,仅限于那些IQueryable、IQueryable等接口
键入foundIface=ifaceQuery.SingleOrDefault();
//希望至少有一个,而且只有一个
if(null==foundIface)//如果我们发现更多,显然不是做出假设的时间和地点
抛出新ArgumentException(“该参数不明确。它实现了0个或多个(不同的)IQueryable特殊化。”);
Type elementType=foundIface.GetGenericArguments()[0];
//我们将typeof(T)从typeof(IQueryable)中去掉
MethodInfo getData\u SpecializedFor\u ElementType=Foo.GenericDefinition\u getData.MakeGenericMethod(ElementType);
//并要求打字系统使我们(或找到我们)具体化
//在**GetData**方法中
试一试{
对象结果=getData\u SpecializedFor\u ElementType.Invoke(
obj:null,
参数:新对象[]{data,bar,bravo}
);
//然后我们调用它(通过反射)
//而忘记了“铸态”的结果
//(即使它是空的,也肯定是可以的)
返回可查询的结果;
}捕获(目标异常){
//万一发生意外,我们假装不在这里
//做这些事吗
抛出ex.InnerException;
//重新思考编辑:实际上是以这种方式重新引用它
//您正在覆盖ex.InnerException的原始堆栈跟踪
//所以,你必须选择你想要的:在大多数情况下,最好不要重复
//特别是当你想改变被抛出的东西时
}
}
}

您是否尝试过像在
dynamic o=Expressin.Lambda中那样制作o
dyanamic
。我认为它会很好地发挥作用。打开了这么多的可能性。我突然想到了这一点,但总觉得这太麻烦了。谢谢,伙计!等等,我会把它作为答案发布;-)一些好东西!我也想到了MakeGenericMethod。谢谢你这么详细和耐心。但是,即使将IQueryable发送到该方法,我也可能会遇到问题,因此我只获取对象来代替IQueryable。你不能做的是把IQueryable q=obj说成IQueryable(首字母缩略词代表某个RuntimeKnown,但语法上是YunKnownType:))哦。哦,我错了。现在我明白了!无论如何从.NET4.0开始,您知道我们有能力将IEnumerable转换为IEnumerable(协方差),并将IComparator转换为IComparator(逆变)。所以简而言之,至少可以(显式地)将结果对象强制转换为IQueryable(因为对象与任何东西都兼容,IQueryable与自身是协变的)
    private static readonly MethodInfo genericDefinitionOf_getData;

    static Foo() {
        Type prototypeQueryable = typeof(IQueryable<int>); 
        // this could be any IQuerable< something >
        // just had to choose one

        MethodInfo getData_ForInts = typeof(Foo).GetMethod(
            name: "GetData", 
            bindingAttr: BindingFlags.Static | BindingFlags.Public,
            binder: Type.DefaultBinder,
            types: new [] { prototypeQueryable, typeof(int), typeof(bool) },
            modifiers: null
        );
        // now we have the GetData<int>(IQueryable<int> data, int bar, bool bravo)
        // reffered by the reflection object getData_ForInts

        MethodInfo definition = getData_ForInts.GetGenericMethodDefinition();
        // now we have the generic (non-invokable) GetData<>(IQueryable<> data, int bar, bool bravo)
        // reffered by the reflection object definition

        Foo.genericDefinitionOf_getData = definition;
        // and we store it for future use
    }
    public static IQueryable GetDataEx(IQueryable data, int bar, bool bravo) {
        if (null == data)
            throw new ArgumentNullException("data");
        // we can't honor null data parameters

        Type typeof_data = data.GetType(); // get the type (a class) of the data object
        Type[] interfaces = typeof.GetInterfaces(); // list it's interfaces

        var ifaceQuery = interfaces.Where(iface => 
            iface.IsGenericType && 
            (iface.GetGenericTypeDefinition() == typeof(IQueryable<>))
        ); // filter the list down to just those IQueryable<T1>, IQueryable<T2>, etc interfaces
        Type foundIface = ifaceQuery.SingleOrDefault();
        // hope there is at least one, and only one

        if (null == foundIface) // if we find more it's obviously not the time and place to make assumptions
            throw new ArgumentException("The argument is ambiguous. It either implements 0 or more (distinct) IQueryable<T> particularizations.");

        Type elementType = foundIface.GetGenericArguments()[0];
        // we take the typeof(T) out of the typeof(IQueryable<T>)

        MethodInfo getData_particularizedFor_ElementType = Foo.genericDefinitionOf_getData.MakeGenericMethod(elementType);
        // and ask the TypeSystem to make us (or find us) the specific particularization
        // of the **GetData < T >** method

        try {
          object result = getData_particularizedFor_ElementType.Invoke(
              obj: null,
              parameters: new object[] { data, bar, bravo }
          );
          // then we invoke it (via reflection)

          // and obliviously "as-cast" the result to IQueryable
          // (it's surely going to be ok, even if it's null)
          return result as IQueryable;

        } catch (TargetInvocationException ex) {
          // in case of any mis-haps we make pretend we weren't here
          // doing any of this
          throw ex.InnerException;

          // rethink-edit: Actually by rethrowing this in this manner
          // you are overwriting the ex.InnerException's original StackTrace
          // so, you would have to choose what you want: in most cases it's best not to rethrow
          // especially when you want to change that which is being thrown
        }
    }


}