2010-03-11 5 views
49

У меня возникла проблема с использованием C# 4.0 с дополнительными параметрами.Вызов методов с дополнительными параметрами через отражение

Как вызвать функцию (или, скорее, конструктор, у меня есть объект ConstructorInfo), для которого я знаю, что он не требует каких-либо параметров?

Вот код, который я использую сейчас:

type.GetParameterlessConstructor() 
    .Invoke(BindingFlags.OptionalParamBinding | 
      BindingFlags.InvokeMethod | 
      BindingFlags.CreateInstance, 
      null, 
      new object[0], 
      CultureInfo.InvariantCulture); 

(Я просто попытался с различными BindingFlags).

GetParameterlessConstructor - специальный способ расширения, который я написал для Type.

ответ

106

Согласно MSDN, чтобы использовать параметр по умолчанию, вы должны пройти Type.Missing.

Если ваш конструктор имеет три необязательных аргумента, то вместо передачи пустого массива объектов вы передадите массив из трех элементов, где значение каждого элемента равно Type.Missing, например.

type.GetParameterlessConstructor() 
    .Invoke(BindingFlags.OptionalParamBinding | 
      BindingFlags.InvokeMethod | 
      BindingFlags.CreateInstance, 
      null, 
      new object[] { Type.Missing, Type.Missing, Type.Missing }, 
      CultureInfo.InvariantCulture); 
+3

Этот ответ на самом деле лучше, чем тот, который отмечен как правильный! –

+0

Я могу подтвердить, что это работает. Я согласен, что это лучшее решение в большинстве случаев и, вероятно, должно быть отмечено как таковое. – N8allan

+0

Можете ли вы просто вызвать «Invoke» (без параметров) в результате «MethodInfo» или «ConstructorInfo»? – Alxandr

22

Дополнительные параметры обозначаются обычным атрибутом и обрабатываются компилятором.
Они не имеют никакого эффекта (кроме флага метаданных) на IL и не поддерживаются напрямую отражением (кроме IsOptional и DefaultValue).

Если вы хотите использовать необязательные параметры с отражением, вам необходимо вручную передать их значения по умолчанию.

+0

спасибо. Я придумал решение, которое делает именно это. Это выглядит не очень красиво, но это работает. Кроме того, IsOptional не является единственным свойством, DefaultValue также существует, поэтому я просто создаю массив из всех DefaultValues. – Alxandr

1

С рамками открытого кода ImpromptuInterface в версии 4 вы можете использовать DLR в C# 4.0 для вызова конструкторов в виде very late bound way и он полностью осознает конструкторы с именем/необязательными аргументами, это работает в 4 раза быстрее, чем Activator.CreateInstance(Type type, params object[] args) и вы не» t должны отражать значения по умолчанию.

using ImpromptuInterface; 
using ImpromptuInterface.InvokeExt; 

...

//if all optional and you don't want to call any 
Impromptu.InvokeConstructor(type) 

или

//If you want to call one parameter and need to name it 
Impromptu.InvokeConstructor(type, CultureInfo.InvariantCulture.WithArgumentName("culture")) 
3

Я просто добавлю код ... потому что. Я не согласен с этим кодом, но он довольно прямолинейный. Надеюсь, это поможет кому-то, кто спотыкается об этом. Испытано, хотя, вероятно, не так хорошо, как вы хотели бы в производственной среде:

Вызов метода имяМетода на объект OBJ с аргументами аргументах:

public Tuple<bool, object> Evaluate(IScopeContext c, object obj, string methodName, object[] args) 
    { 
     // Get the type of the object 
     var t = obj.GetType(); 
     var argListTypes = args.Select(a => a.GetType()).ToArray(); 

     var funcs = (from m in t.GetMethods() 
        where m.Name == methodName 
        where m.ArgumentListMatches(argListTypes) 
        select m).ToArray(); 

     if (funcs.Length != 1) 
      return new Tuple<bool, object>(false, null); 

     // And invoke the method and see what we can get back. 
     // Optional arguments means we have to fill things in. 
     var method = funcs[0]; 
     object[] allArgs = args; 
     if (method.GetParameters().Length != args.Length) 
     { 
      var defaultArgs = method.GetParameters().Skip(args.Length) 
       .Select(a => a.HasDefaultValue ? a.DefaultValue : null); 
      allArgs = args.Concat(defaultArgs).ToArray(); 
     } 
     var r = funcs[0].Invoke(obj, allArgs); 
     return new Tuple<bool, object>(true, r); 
    } 

и функция ArgumentListMatches ниже, которая в основном берет место логики, вероятно, найдено в GetMethod:

public static bool ArgumentListMatches(this MethodInfo m, Type[] args) 
    { 
     // If there are less arguments, then it just doesn't matter. 
     var pInfo = m.GetParameters(); 
     if (pInfo.Length < args.Length) 
      return false; 

     // Now, check compatibility of the first set of arguments. 
     var commonArgs = args.Zip(pInfo, (margs, pinfo) => Tuple.Create(margs, pinfo.ParameterType)); 
     if (commonArgs.Where(t => !t.Item1.IsAssignableFrom(t.Item2)).Any()) 
      return false; 

     // And make sure the last set of arguments are actually default! 
     return pInfo.Skip(args.Length).All(p => p.IsOptional); 
    } 

Много LINQ, и это не было проверено на работоспособность!

Кроме того, это не будет обрабатывать вызовы общей функции или метода. Это делает это значительно более уродливым (как в повторных вызовах GetMethod).

1

Все вопросы исчезают, как вы видите, что ваш код декомпилированы:

C#:

public MyClass([Optional, DefaultParameterValue("")]string myOptArg) 

MSIL:

.method public hidebysig specialname rtspecialname instance void .ctor([opt]string myOptArg) cil managed 

Как вы видите, необязательный параметр представляет собой реальный отдельный объект, который украшен с конкретными атрибутами, и их необходимо уважать при вызове посредством отражения, как описано выше.

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