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
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中那样制作odyanamic
。我认为它会很好地发挥作用。打开了这么多的可能性。我突然想到了这一点,但总觉得这太麻烦了。谢谢,伙计!等等,我会把它作为答案发布;-)一些好东西!我也想到了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
}
}
}