Как использовать отражение для вызова частного метода?



в моем классе есть группа частных методов, и мне нужно вызвать один динамически на основе входного значения. И вызывающий код, и целевые методы находятся в одном экземпляре. Код выглядит так:



MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType);
dynMethod.Invoke(this, new object[] { methodParams });


в этом случае GetMethod() не возвращает частные методы. Что BindingFlags мне нужно поставить, чтобы GetMethod() Так что он может найти частные методы?

614   11  

11 ответов:

просто измените свой код, чтобы использовать перегруженный версия GetMethod который принимает BindingFlags:

MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType, 
    BindingFlags.NonPublic | BindingFlags.Instance);
dynMethod.Invoke(this, new object[] { methodParams });

здесь документация перечисления BindingFlags.

BindingFlags.NonPublic не даст результатов сам по себе. Как оказалось, сочетая его с BindingFlags.Instance делает трюк.

MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType, 
    BindingFlags.NonPublic | BindingFlags.Instance);

и если действительно хотите попасть в беду, сделать его легче выполнить, написав метод расширения:

static class AccessExtensions
{
    public static object call(this object o, string methodName, params object[] args)
    {
        var mi = o.GetType ().GetMethod (methodName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance );
        if (mi != null) {
            return mi.Invoke (o, args);
        }
        return null;
    }
}

и использование:

    class Counter
    {
        public int count { get; private set; }
        void incr(int value) { count += value; }
    }

    [Test]
    public void making_questionable_life_choices()
    {
        Counter c = new Counter ();
        c.call ("incr", 2);             // "incr" is private !
        c.call ("incr", 3);
        Assert.AreEqual (5, c.count);
    }

вы абсолютно уверены, что это не может быть сделано через наследование? Отражение-это самое последнее, на что вы должны смотреть при решении проблемы, это затрудняет рефакторинг, понимание вашего кода и любой автоматизированный анализ.

похоже, у вас должен быть только класс DrawItem1, DrawItem2 и т. д., который переопределяет ваш dynMethod.

Microsoft недавно изменен API отражения большинство этих ответов устарели. Следующее должно работать на современных платформах (включая Xamarin.Формы и UWP):

obj.GetType().GetTypeInfo().GetDeclaredMethod("MethodName").Invoke(obj, yourArgsHere);

или как метод расширения:

public static object InvokeMethod<T>(this T obj, string methodName, params object[] args)
{
    var type = typeof(T);
    var method = type.GetTypeInfo().GetDeclaredMethod(methodName);
    return method.Invoke(obj, args);
}

Примечание:

  • если нужный метод в суперклассе obj the T generic должен быть явно установлен в тип суперкласса.

  • если метод асинхронный вы можете использовать await (Task) obj.InvokeMethod(…).

отражение особенно на частных членов не так

  • отражение нарушает безопасность типов. вы можете попробовать вызвать метод, который не существует (больше), или с неправильными параметрами, или с слишком большим количеством параметров, или недостаточно... или даже в неправильном порядке (это мой любимый :) ). Кстати, тип возврата также может измениться.
  • рефлексия-это медленно.

отражение частных членов перерывы инкапсуляция принцип и, таким образом, подвергая свой код следующим образом :

  • увеличение сложности вашего кода, потому что он должен обрабатывать внутреннее поведение классов. То, что скрыто, должно оставаться скрытым.
  • делает ваш код легко сломать как он будет компилироваться, но не будет работать, если метод изменил свое название.
  • делает частный код легко сломать потому что если это является частным он не предназначен для того, чтобы называться таким образом. Возможно, частный метод ожидает некоторого внутреннего состояния перед вызовом.

что, если я все равно должен это сделать ?

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

если вы это сделаете, сделать это правильно

  • смягчить легко сломать:

чтобы смягчить проблему с легким разрывом, лучше всего обнаружить любой потенциальный разрыв путем тестирования в модульных тестах, которые будут выполняться в сборке непрерывной интеграции или такой. Конечно, это означает, что вы всегда используете одну и ту же сборку (которая содержит закрытые члены). Если вы используете динамическую нагрузку и отражение, вам нравится играть с огнем, но вы всегда можете поймать исключение, которое вызов может производить.

  • смягчить медлительность отражения:

в последних версиях .Net Framework, CreateDelegate бить с коэффициентом 50 MethodInfo invoke:

// The following should be done once since this does some reflection
var method = this.GetType().GetMethod("Draw_" + itemType, 
  BindingFlags.NonPublic | BindingFlags.Instance);

// Here we create a Func that targets the instance of type which has the 
// Draw_ItemType method
var draw = (Func<TInput, Output[]>)_method.CreateDelegate(
                 typeof(Func<TInput, TOutput[]>), this);

draw звонки будут примерно в 50 раз быстрее, чем MethodInfo.Invoke используйте draw как стандарт Func вот так:

var res = draw(methodParams);

проверить это мой пост чтобы увидеть бенчмарк на разных вызовах метода

не могли бы вы просто иметь другой метод рисования для каждого типа, который вы хотите нарисовать? Затем вызовите перегруженный метод Draw, передающий объект типа itemType для рисования.

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

Я думаю, что вы можете передать его BindingFlags.NonPublic здесь это - это GetMethod метод.

BindingFlags.NonPublic

вызывает любой метод, несмотря на его уровень защиты на экземпляр объекта. Наслаждайтесь!

public static object InvokeMethod(object obj, string methodName, params object[] methodParams)
{
    var methodParamTypes = methodParams?.Select(p => p.GetType()).ToArray() ?? new Type[] { };
    var bindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
    MethodInfo method = null;
    var type = obj.GetType();
    while (method == null && type != null)
    {
        method = type.GetMethod(methodName, bindingFlags, Type.DefaultBinder, methodParamTypes, null);
        type = type.BaseType;
    }

    return method?.Invoke(obj, methodParams);
}

прочитайте этот (дополнительный) ответ (который иногда является ответом), чтобы понять, куда это идет и почему некоторые люди в этой теме жалуются, что "он все еще не работает"

Я написал точно такой же код как один из ответов здесь. Но у меня все еще была проблема. Я поставил точку останова на

var mi = o.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance );

Он выполнил, но mi == null

и он продолжал вести себя так, пока я не "перестроил" все проекты вовлеченный. Я тестировал одну сборку, в то время как метод отражения сидел в третьей сборке. Это было совершенно запутанно, но я использовал Immediate Window для обнаружения методов, и я обнаружил, что частный метод, который я пытался провести модульный тест, имел старое имя (я переименовал его). Это сказало мне, что старая сборка или PDB все еще там, даже если модульный тестовый проект строится - по какой-то причине проект, который он тестирует, не был построен. "перестроить" работал

Comments

    Ничего не найдено.