2009-09-04 2 views
2

Код F# не работает, потому что Type.DefaultBinder не хочет связываться с общим методом Id. Есть ли альтернатива Binder, которая сделает это?Есть ли System.Reflection.Binder (.NET), который связывается с универсальными методами?

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"} 
     ); 
    } 
} 
+0

пожалуйста, вы можете удалить C# тег –

+2

уверен. не могли бы вы объяснить, почему? Я новичок в Stackoverflow и еще не знаю всех правил, но я думаю, что вопрос не является специфичным для языка - так что если тег F # есть, почему бы и не C#? – t0yv0

ответ

0

В записке в разделе «Примечания» на InvokeMember page указывает, что 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 нарушает все виды ожидаемых инвариантов, хотя, поскольку она возвращает метод, который не был передан в качестве кандидата.

+0

Да - взяв его дальше, связующее может выполнять унификацию по типам аргументов, чтобы определить общие параметры метода. Однако это, вероятно, окажется несовместимым с разрешением метода по умолчанию, используемым компиляторами. Я думал, что, поскольку компиляторы C#/F # делают это во время компиляции, возможно, есть совместимый Binder - но похоже, что нет. – t0yv0

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  
0

Вот конкретное решение 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; 
    } 

 Смежные вопросы

  • Нет связанных вопросов^_^