System.Reflection.Binder(.NET)は、一般的な方法に結合することがありますか?

StackOverflow https://stackoverflow.com/questions/1380317

質問

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"}
        );
    }
}
役に立ちましたか?

解決

InvokeMemberページの "備考" の下の注記を示しInvokeMemberは、一般的なメソッドを呼び出すために使用することはできません。おそらくこれは、あなたが何らかの形で一般的なパラメータを指定する必要があるため、あなたは、どちらか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:シンプルとNOTパフォーマンスがバインド私のニーズは、私はちょうどそうワーキングソリューションを必要とし、そのノートに、私は、これは大きな調整が必要とのギャップを持っていることを承知しています。すべてのフィードバックを歓迎してもされています。

私はこれがあなたの問題に役立ちます願っています。

    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