هل هناك نظام.
-
21-09-2019 - |
سؤال
ما يلي F#
الرمز فشل لأن Type.DefaultBinder
لا يريد أن يرتبط بالعامة Id
طريقة. هل هناك بديل Binder
هذا من شأنه أن يفعل هذا؟
open System
open System.Reflection
type Foo() =
member this.Id<'T>(x: 'T) : 'T = x //'
typeof<Foo>.InvokeMember (
"F",
BindingFlags.InvokeMethod,
Type.DefaultBinder,
(new Foo()),
[| box "test" |]
)
هنا ما يعادل C#:
using System;
using System.Reflection;
public class Foo {
T Id<T>(T x) {
return x;
}
static void Main() {
typeof(Foo).InvokeMember
(
"F",
BindingFlags.InvokeMethod,
Type.DefaultBinder,
(new Foo()),
new object[] {"test"}
);
}
}
المحلول
الملاحظة تحت "الملاحظات" على صفحة invokemmed يشير إلى أنه لا يمكن استخدام vokemsember لاستدعاء طريقة عامة. من المفترض أن هذا يتعلق بحقيقة أنه لا يمكنك استخدامه typeof<Foo>.GetMethod("Id").Invoke(...)
إما ، نظرًا لأنك تحتاج إلى تحديد معلمة عامة بطريقة أو بأخرى.
من ناحية أخرى ، يبدو أنه من المحتمل أن تخترق شيئًا معًا لديه لقطة في العمل:
type MyBinder() =
inherit System.Reflection.Binder() with
let bnd = System.Type.DefaultBinder
override x.SelectProperty(a,b,c,d,e) = bnd.SelectProperty(a,b,c,d,e)
override x.ChangeType(a,b,c) = bnd.ChangeType(a,b,c)
override x.BindToField(a,b,c,d) = bnd.BindToField(a,b,c,d)
override x.ReorderArgumentArray(a,b) = bnd.ReorderArgumentArray(&a,b)
override x.SelectMethod(a,b,c,d) = bnd.SelectMethod(a,b,c,d)
override x.BindToMethod(a,meths,args,b,c,d,e) =
try
bnd.BindToMethod(a,meths,&args,b,c,d,&e)
with _ ->
let [| meth |],[| arg |] = meths,args
upcast (meth :?> System.Reflection.MethodInfo).MakeGenericMethod([| arg.GetType() |])
هذا يتعامل فقط مع الأساليب العامة غير المحملة بحجة واحدة ، ولكن يمكنك محاولة جعلها أكثر قوة. لن أتفاجأ إذا كان هذا التنفيذ لـ BindTomethod يكسر جميع أنواع الثوابات المتوقعة ، لأنه يعيد طريقة لم يتم تمريرها كمرشح.
نصائح أخرى
هذا لا يجيب مباشرة على سؤالك ، ولكن إذا كان هدفك النهائي هو فقط الاتصال بالطريقة ، فيمكنك القيام على سبيل المثال
open System
open System.Reflection
type Foo() =
member this.Id<'T>(x: 'T) : 'T = x // '
let ms = typeof<Foo>.GetMethods()
|> Array.filter (fun m -> m.Name="Id" && m.GetGenericArguments().Length=1)
assert( ms.Length = 1 )
let m = ms.[0]
let r = m.MakeGenericMethod([|typeof<string>|]).Invoke(new Foo(),[|box "test"|])
printfn "%A" r
فيما يلي حل C# محدد لإيجاد طريقة تمديد عام ويمكن تعديله لتنفيذ الموثق. FYI: احتياجاتي حيث تكون بسيطة وغير الأداء ملزمة ، لقد احتجت فقط إلى حل عمل ، لذلك ، في تلك الملاحظة ، أدرك أن هذا يحتاج إلى ضبط كبير وقد يكون له فجوات. أي ردود فعل موضع ترحيب رغم ذلك.
آمل أن يساعد هذا في مشكلتك
private MethodInfo FindExtensionMethod(Type instancetype, string methodName, Expression[] args)
{
Type[] parametertypes = Enumerable.Repeat(instancetype, 1).Concat(args.Cast<ConstantExpression>().Select(a => a.Value.GetType())).ToArray();
var methods = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes().Where(t => t.IsSealed && !t.IsGenericType && !t.IsNested))
.SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(m => m.IsDefined(typeof(ExtensionAttribute), false)
&& m.Name == methodName
&& CanBeInvokedWith(m, parametertypes))
.Select(m => EnsureInvokableMethodFor(m, parametertypes)))
.ToList();
return methods.FirstOrDefault();
}
private MethodInfo EnsureInvokableMethodFor(MethodInfo method, Type[] parameterTypes)
{
if (method.ContainsGenericParameters)
{
var genericparams = GetGenericParametersFor(method, parameterTypes).ToArray();
MethodInfo nongenric = method.MakeGenericMethod(genericparams);
return nongenric;
}
else
return method;
}
private IEnumerable<Type> GetGenericParametersFor(MethodInfo method, Type[] parameterTypes)
{
IDictionary<int, Type> args = new Dictionary<int, Type>();
List<Type> genargs = new List<Type>(method.GetGenericArguments());
int i = 0;
foreach (var parameter in method.GetParameters())
{
if (parameter.ParameterType.IsGenericParameter)
{
AddGenArgs(args,
genargs.IndexOf(parameter.ParameterType),
parameterTypes[i]);
}
else
{
if (parameter.ParameterType.IsGenericType)
{
int j = 0;
foreach (Type genarg in parameter.ParameterType.GetGenericArguments())
{
if (genarg.IsGenericParameter)
{
AddGenArgs(args,
genargs.IndexOf(genarg),
parameterTypes[i].GetGenericArguments()[j]);
}
j++;
}
}
}
i++;
}
return args.Values;
}
private static void AddGenArgs(IDictionary<int, Type> args, int argindex, Type arg)
{
if (args.ContainsKey(argindex))
{
if (args[argindex] != arg)
throw new ArgumentOutOfRangeException();
}
else
args[argindex] = arg;
}
private bool CanBeInvokedWith(MethodInfo method, Type[] parametertypes)
{
var parameters = method.GetParameters();
if (parameters.Length != parametertypes.Length)
return false;
int i = 0;
return parameters.All(p => CanBeAssignedFrom(p.ParameterType, parametertypes[i++]));
}
private bool CanBeAssignedFrom(Type paramType, Type argType)
{
if (paramType.IsGenericType)
{
if (argType.IsGenericType)
{
if (paramType.GetGenericTypeDefinition() == argType.GetGenericTypeDefinition())
{
return GenericArgsAreCompatible(
paramType.GetGenericArguments(),
argType.GetGenericArguments());
}
else
return false;
}
else
return false;
}
else
{
if (paramType.IsGenericParameter)
return true;
else
return paramType.IsAssignableFrom(argType);
}
}
private bool GenericArgsAreCompatible(Type[] paramArgs, Type[] argArgs)
{
if (paramArgs.Length != argArgs.Length)
return false;
int i = 0;
return paramArgs.All(p => TypesAreCompatible(p, argArgs[i++]));
}
private bool TypesAreCompatible(Type paramArg, Type argArg)
{
if (paramArg.IsGenericParameter)
return true;
else
return paramArg == argArg;
}