【问题标题】:Is there a System.Reflection.Binder (.NET) that binds to generic methods?是否有绑定到泛型方法的 System.Reflection.Binder (.NET)?
【发布时间】:2009-09-04 16:36:50
【问题描述】:

以下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"}
        );
    }
}

【问题讨论】:

  • 你能把c#标签去掉吗
  • 当然。你能解释一下为什么吗?我是 Stackoverflow 的新手,还不知道所有规则,但我认为这个问题并不是特定于语言的 - 所以如果有 F# 标签,为什么不使用 C#?

标签: .net reflection f# late-binding


【解决方案1】:

这并不能直接回答您的问题,但如果您的最终目标只是调用该方法,您可以这样做,例如

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    

【讨论】:

    【解决方案2】:

    InvokeMember page 上“备注”下的注释表明 InvokeMember 不能用于调用泛型方法。大概这与您不能使用typeof&lt;Foo&gt;.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 的这种实现破坏了所有类型的预期不变量,我不会感到惊讶,因为它返回的方法没有作为候选方法传入。

    【讨论】:

    • 是的——更进一步,绑定器可以对参数类型执行统一以确定方法的通用参数。但是,这最终可能与编译器使用的默认方法解析不兼容。我认为由于 C#/F# 编译器在编译时执行此操作,因此可能有一个兼容的 Binder - 但看起来没有。
    【解决方案3】:

    这是一个用于查找通用扩展方法的特定 C# 解决方案,可以对其进行修改以实现活页夹。仅供参考:我的需求简单且不受性能限制,我只需要一个可行的解决方案,因此,在这点上,我知道这需要进行重大调整并且可能存在差距。欢迎任何反馈。

    希望对您的问题有所帮助

        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;
        }
    

    【讨论】:

      猜你喜欢
      • 2011-03-20
      • 2012-12-11
      • 1970-01-01
      • 2023-04-10
      • 1970-01-01
      • 2019-12-15
      • 2011-04-15
      • 1970-01-01
      相关资源
      最近更新 更多