Замена тела метода другим телом метода - PullRequest
0 голосов
/ 24 апреля 2011

То, что я хочу сделать, это заменить тело (статического) метода на тело другого метода, сохранив тело исходного метода.

Позвольте мне начать с примера:

public static class Program
{
    public static void Main(string[] args)
    {
        switch (new Random().Next(3))
        {
            case 0:
                // Change MainFunction's body into a's body, storing the original body somewhere else
                break;
            case 1:
                // Change MainFunction's body into b's body, storing the original body somewhere else
                break;
            case 2:
                // Change MainFunction's body into c's body, storing the original body somewhere else
                break;
        }
        MainFunction(null);
    }

    public static void MainFunction(object someParameter)
    {
        Console.Write("The method called is: ");
    }

    private static void a(object someParameter)
    {
        // Call the 'base' method
        Console.WriteLine("a");
    }

    private static void b(object someParameter)
    {
        // Call the 'base' method
        Console.WriteLine("c");
    }

    private static void c(object someParameter)
    {
        // Call the 'base' method
        Console.WriteLine("c");
    }
}

Ожидаемый результат: «Вызванный метод:» и «a», «b» или «c». Я знаю, что этот пример совершенно бесполезен, и поэтому он является примером, но я просто хочу знать, как этого добиться.

Кто-нибудь знает, как этого добиться?

EDIT:
Я не ищу делегата, я знаю, что они очень полезны, но, как я уже сказал, это пример, и я знаю, как этого добиться.

Я также знаю, что вам следует повторно использовать случайные числа, чтобы избежать накладных расходов при пересчете исходных данных и дать GC немного отдыха.

Пример только для демонстрации результата, и, конечно, этот пример может быть легко решен с помощью delegate, Action<T> или, если вы хотите, чтобы возвращались значения также Func<TOut>, но я не ищу это решение .

Ответы [ 5 ]

4 голосов
/ 24 апреля 2011

Похоже, что вы действительно хотите, чтобы здесь был делегат.

Вы можете создать делегат типа Action<object> и назначить его в своем коде, а затем вызвать делегата в MainFunction в зависимости от ситуации.

private Action<object> MainFunction;

private Random rand = new Random();

public static void Main(string[] args)
{
    switch (rand.Next(3))
    {
        case 0: MainFunction = a; break;
        case 1: MainFunction = b; break;
        case 2: MainFunction = c; break;
    }
    MainFunction(null);
}

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

РЕДАКТИРОВАТЬ: Я думаю, что вам нужно дать лучшее объяснение того, почему подход с использованием делегатов является неадекватнымдля вашей проблемы. Определение ключевых требований, которые вам нужны, и полный, соответствующий пример помогут вам получить ответ, который вас удовлетворит.В общем случае невозможно заменить тело метода существующего класса во время выполнения - это нарушит модель безопасности CLR.Лучшее, что вы можете сделать, - это потенциально генерировать новый класс во время выполнения и генерировать методы с соответствующими сигнатурами и телами - однако мне неясно, чем это будет отличаться от использования делегата.

В качестве альтернативы, это может бытьВы можете использовать что-то вроде PostSHARP и использовать аспектно-ориентированные методы, чтобы изменить поведение MainFunction () во время выполнения.Например, MethodBoundaryAspect в PostSHARP позволяет запускать произвольный код до, после или вместо метода.Однако, без более точного объяснения , какую именно проблему вы пытаетесь решить , лучшее, что мы можем сделать, - это угадать ... что не даст вам очень далеко.

3 голосов
/ 24 апреля 2011

C # является статическим типом языка.Это означает, что компилятор читает ваш код C # и превращает его в файл .exe или .dll на языке ассемблера.

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

Движок времени выполнения использует нисходящую логику;он начинается в начале вашей программы (метод Main) и выполняет каждый оператор, по одному за раз.Например, когда он сталкивается с эквивалентом «Call MyMethod» на ассемблере, он просто переходит к первому оператору в фрагменте кода, связанному с «MyMethod», и начинает выполняться оттуда.После того, как все операторы в блоке кода «MyMethod» были выполнены, механизм выполнения возвращается туда, где он был до вызова MyMethod.

Итак, 3 основных цели имени метода:

  1. Разрешение вам и другим программистам, использующим ваш код или сборку, идентифицировать метод и отличить его от других методов
  2. Разрешение компилятору узнать, какой метод уровня сборки связан с вызовами методов в вашем исходном коде
  3. И, наконец, выполнение связанного с методом фрагмента кода во время выполнения.

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

Возвращаясь к статической природе C #, вы пытаетесь сделать более распространенную функцию языков с динамической типизацией, таких как JavaScript, PHP, Lua и другие.Например, в JavaScript вы можете определить функцию (эквивалентную методу JavaScript), присвоить ее переменной, затем изменить значение переменной так, как вы хотите, и вызвать ее на лету.Эта функциональность становится возможной благодаря тому, как выполняется код: в отличие от C # и других языков со статической типизацией, каждый оператор читается как читаемый человеком исходный код, интерпретируется в один или несколько операторов исполняемого машиночитаемого кода и выполняется,и двигатель времени выполнения переходит к следующему оператору (или к началу вызова функции, в зависимости от обстоятельств).

2 голосов
/ 10 мая 2013

Если статический метод определен в динамическом модуле или сборке, вы можете использовать System.Reflection.Emit.MethodRental.SwapMethodBody, чтобы делать именно то, что вы хотите, но он работает только с методами из динамических модулей.

2 голосов
/ 24 апреля 2011

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

public static Action<object> MethodToCall;

Тогда в случае 0, например:

MethodToCall = (someParameter) => {
    MainFunction(someParameter);
    a(someParameter);
}

И тогда вместо MainFunction (null) вы вызываете:

MethodToCall(null)
1 голос
/ 24 апреля 2011

Вы ищете Action?(отличный заголовок)

public static Random rand = new Random();

public static void Main(string[] args)
{
    Action<string[]> action = null;
    switch (rand.Next(3))
    {
        case 0:
            action = a;
            break;
        case 1:
            action = b;
            break;
        case 2:
            action = c;
            break;
    }
    action(null);
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...