سؤال

ما يلي 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;
    }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top