[英]Dynamically infer generic types via. reflection
任何幫助將在這一個上受到贊賞。 我正在嘗試在靜態類型上實現動態對象包裝器。 然后,這個包裝器允許我在運行時動態調用靜態函數。
例如:
dynamic obj = new StaticDynamicWrapper(typeof(MyStaticType));
obj.DoSomething(arg1);
這就是主意。 我得到了一些在線參考,讓它在非泛型方法和屬性上工作,但在“DoSomething”實際上是一種通用方法時遇到了各種問題。
例如,如果“DoSomething”的聲明如下:
public static RetType DoSomething<TArg>(this TArg arg1);
或者甚至更糟糕的是,如果做了一些過載,請使用以下簽名...
public static RetType DoSomething<TArg>(this OtherGenericType<AnotherGenericType<TArg>> arg1);
因此,我需要實現DynamicObject.TryInvokeMember,方法invokation能夠在運行時根據運行時參數(即object [] args)推斷,這是DoSomething的正確封閉泛型方法。 換句話說,我希望能夠選擇正確的重載並確定調用MakeGenericMethod的正確類型參數,所有這些都在運行時。
到目前為止,最大的障礙是弄清楚如何將方法的開放泛型參數映射到參數參數聲明的閉合類型參數(即object [] args)。 任何人都可以幫助我嗎?
謝謝你的時間!
DLR將推斷方法重載,包括泛型。 通過nuget提供的開源框架ImpromptuInterface簡化了對單個方法調用的dlr調用 ,並為您完成所有工作。 事實上,它有一個基類ImpromptuForwarder ,它將通過一個小的構造函數更改來完成此操作。
using ImpromptuInterface;
using ImpromptuInterface.Dynamic;
public class StaticDynamicWrapper:ImpromptuForwarder{
public StaticDynamicWrapper(Type target):base(InvokeContext.CreateStatic(target)){
}
}
通過閱讀jbtule的答案中提到的ImpromptuInterface代碼,您可以使用DLR這樣做:
public override bool TryInvokeMember(
InvokeMemberBinder binder, object[] args, out object result)
{
var innerBinder = Binder.InvokeMember(
CSharpBinderFlags.None, binder.Name, null, null,
new[]
{
CSharpArgumentInfo.Create(
CSharpArgumentInfoFlags.UseCompileTimeType
| CSharpArgumentInfoFlags.IsStaticType, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
});
var callSite = CallSite<Func<CallSite, object, object, object>>
.Create(innerBinder);
try
{
result = callSite.Target(callSite, m_type, args[0]);
return true;
}
catch (RuntimeBinderException)
{
result = null;
return false;
}
}
此代碼僅適用於具有一個參數的方法。 如果要支持更多這些,則必須將更多CSharpArgumentInfo
放入集合中,使用適當數量的Func
委托參數創建CallSite
,然后調用它。 ( ImpromptuInterface
使用了一個switch
。)
對於那些需要此功能但不想或無法為其項目添加外部依賴項的人,以下內容可能會對您有所幫助。 它不像即興解決方案那樣強大,但可能正是您所需要的。 它最好只有9個參數(我認為,無論System.Func類型的最大靜態參數是什么)。
使用它需要您自擔風險 。 經測試只能在我的電腦上工作! ;)
public class StaticDynamicWrapper : DynamicObject
{
private Type _type;
public StaticDynamicWrapper(Type type) { _type = type; }
private static readonly IList< Func<Type, string, object[],object>> CallSiteInvokers;
/// <summary>
/// Static initializer. Used to improve performance so we only need to resolve the right Func type, once.
/// </summary>
static StaticDynamicWrapper()
{
CallSiteInvokers = new List< Func< Type, string, object[],object>>();
//Get the max number of arguments allowed by the built in Func types.
var funcTypes = Assembly.GetAssembly(typeof (Func<>)).GetTypes().Where(t => t.Name.StartsWith("Func`"))
.Concat(Assembly.GetAssembly(typeof (Func<,,,,,,,,,,,,>)).GetTypes().Where(t => t.Name.StartsWith("Func`")))
.OrderBy(t => t.GetGenericArguments().Count()).ToArray();
int maxNoOfArgs = funcTypes.Max(t => t.GetGenericArguments().Length) - 2; //We need to subtract 3 from the absolute max to account for the return type and the 2 required parameters: callsite and target type. Plus 1 to offset the indexing
//Index the function calls based on the number of parameters in the arguments.
for(int i = 0; i < maxNoOfArgs; i++)
{
int funcIndex = i + 2;
CallSiteInvokers.Add
(
( type, name ,objects) =>
{
//The call site pre/post fixes.
var funcGenericArguments = new List<Type>() { typeof(CallSite), typeof(object), typeof(object) };
//The argument info collection
var argumentInfoCollection = new List<CSharpArgumentInfo>()
{
CSharpArgumentInfo.Create(
CSharpArgumentInfoFlags.UseCompileTimeType |
CSharpArgumentInfoFlags.IsStaticType,
null)
};
//Set up the generic arguments for objects passed in.
funcGenericArguments.InsertRange(2,objects.Select(o => o.GetType()));
//Set up the argument info for the inner binder.
argumentInfoCollection.AddRange(objects.Select(o=> CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)));
var innerBinder = Binder.InvokeMember(
CSharpBinderFlags.None, name, null, null, argumentInfoCollection.ToArray()
);
//Dynamically instantiate the generic CallSite, by calling on the "Create" factory method.
var callSite = typeof (CallSite<>)
.MakeGenericType(
funcTypes[funcIndex]
.MakeGenericType(funcGenericArguments.ToArray())
)
.GetMethod("Create")
.Invoke(null,new object[]{innerBinder});
//Dynamically invoke on the callsite target.
object invokingDelegate = callSite.GetType().GetField("Target").GetValue(callSite);
return invokingDelegate.GetType().GetMethod("Invoke").Invoke(invokingDelegate,
new object[]
{
callSite,
type
}.Concat(objects).ToArray());
}
);
}
}
/// <summary>
/// Handle static property accessors.
/// </summary>
/// <param name="binder"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
PropertyInfo prop = _type.GetProperty(binder.Name, BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public);
if (prop == null)
{
result = null;
return false;
}
result = prop.GetValue(null, null);
return true;
}
/// <summary>
/// Handle static methods
/// </summary>
/// <param name="binder"></param>
/// <param name="args"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
try
{
result = CallSiteInvokers[args.Length](_type, binder.Name, args);
return true;
}
catch (RuntimeBinderException)
{
result = null;
return false;
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.