Патч для обезьян в C #

Можно ли расширить или изменить код класса C # во время выполнения?

Мой вопрос конкретно вращается вокруг Monkey Patching / Duck Punching или Meta Object Programming (MOP), как это происходит в языках сценариев, таких как Groovy, Ruby и т. Д.


person humblecoder    schedule 27.12.2010    source источник
comment
Однако вы можете пойти по пути IronRuby (или братьев и сестер), чтобы получить динамические функции в C #.   -  person Gishu    schedule 27.12.2010
comment
Я знаю, что это древний вопрос. Но на самом деле должна быть ссылка на Microsoft Fakes, как я предполагаю. одна из причин, по которой люди обезьяны patch на этих языках тестируют. Также стоит взглянуть на Moqs.   -  person Nathan Cooper    schedule 24.12.2014
comment
Вы можете сделать это с помощью некоторой AOP Framework. Я активно работаю над одним из них здесь: NConcern .NET Runtime AOP Framework Вы можете проверить образец в этом посте : образец патча обезьяны   -  person Tony THONG    schedule 09.12.2016


Ответы (4)


Можно ли расширить или изменить код класса C # во время выполнения?

Нет, это невозможно сделать в .NET. Вы можете написать производные классы и переопределить методы (если они виртуальные), но вы не можете изменить существующий класс. Только представьте, было ли возможно то, о чем вы просили: вы могли бы изменить поведение некоторых существующих системных классов, таких как System.String.

Вы также можете ознакомиться с методами расширения, чтобы добавить функциональность к существующему класс.

person Darin Dimitrov    schedule 27.12.2010
comment
Я думаю, это именно то, что ищет humblecoder. например Добавление метода к типу String для синтаксического анализа настраиваемого типа из строкового представления, чтобы вы могли var serialObject = "STB-2".ToMySerial() - person Gishu; 27.12.2010
comment
@Gishu, да, мы могли бы использовать методы расширения для добавления методов к существующим классам, но мы не можем изменять существующие методы. - person Darin Dimitrov; 27.12.2010
comment
. правильно. Методы расширения могут работать для некоторых нужд ... однако открытие класса, как в Ruby, по-прежнему невозможно, как вы ответили - это то, что, похоже, будет после OP. - person Gishu; 27.12.2010
comment
Я хочу добавлять методы динамически, как это делается в Grails / Ruby Active Record. - person humblecoder; 27.12.2010
comment
AFAIK можно изменить методы некоторых классов во время выполнения или даже вы можете изменить классы во время выполнения, но для этого необходимы глубокие знания clr и изменения структур памяти во время выполнения. - person Vahid K.; 06.03.2017
comment
Только представьте, что будет, - не нужно представлять. Python позволяет это сделать, и хотя это всегда бэкдор, и никто никогда не будет по-настоящему счастлив, когда в этом возникнет необходимость, это чрезвычайно мощный инструмент для решения реальных проблем. Идея о том, что нельзя допускать чего-либо из-за возможности неправильного использования, не является способом разработки языка. - person Glenn Maynard; 29.07.2017
comment
Установка Monkey patching позволяет вам имитировать такие сервисы, как облачные приложения и ресурсы, которые затем позволят вам запускать содержательные модульные тесты для вашего кода без необходимости добавлять особые случаи в ваш производственный код. И это всего лишь один пример того, насколько мощным и полезным является исправление обезьян. См. moto пакет Python, который имитирует большое количество сервисов AWS, чтобы вы могли тестировать свой готовый к отправке код, не загрязняя его хитростями и приборами для модульного тестирования. - person Luis Artola; 27.08.2017
comment
@GlennMaynard Это именно то, как вы разрабатываете язык, если ожидаете, что он будет использоваться для долговременного кода, поддерживаемого десятками разных людей, которые не знают обо всех причудах, вносимых monkeypatching - person Basic; 02.08.2018

Вы можете добавить функциональные возможности, но не можете изменять или удалять функциональные возможности.

person Jeff Hubbard    schedule 27.12.2010
comment
Как это возможно? Иллюстрация приветствуется? - person humblecoder; 27.12.2010
comment
Методы расширения позволяют добавлять методы к объектам (так реализована функциональная сторона LINQ). Приведенные ссылки содержат всю необходимую информацию. - person Jeff Hubbard; 27.12.2010
comment
Методы расширения позволяют добавлять функции только во время компиляции, но не во время выполнения. Если вы собираетесь добавить функциональность во время выполнения, вам необходимо работать с объектом, производным от DynamicObject. - person Michael Lang; 01.04.2011
comment
Хороший улов. Каким-то образом я перешагнул ту часть времени выполнения. - person Jeff Hubbard; 02.04.2011

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

Дополнительные сведения см. В методах расширения в Руководстве по программированию на C #.

person plaes    schedule 27.12.2010

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

Копирование примера из их введения, если у вас есть существующий класс, например:

public class SomeGameClass
{
    public bool isRunning;
    public int counter;

    private int DoSomething()
    {
        if (isRunning)
        {
            counter++;
        }
        return counter * 10;
    }
}

Тогда Harmony может исправить это так:

using HarmonyLib;
using Intro_SomeGame;

public class MyPatcher
{
    // make sure DoPatching() is called at start either by
    // the mod loader or by your injector

    public static void DoPatching()
    {
        var harmony = new Harmony("com.example.patch");
        harmony.PatchAll();
    }
}

[HarmonyPatch(typeof(SomeGameClass))]
[HarmonyPatch("DoSomething")]
class Patch01
{
    static AccessTools.FieldRef<SomeGameClass, bool> isRunningRef =
        AccessTools.FieldRefAccess<SomeGameClass, bool>("isRunning");

    static bool Prefix(SomeGameClass __instance, ref int ___counter)
    {
        isRunningRef(__instance) = true;
        if (___counter > 100)
            return false;
        ___counter = 0;
        return true;
    }

    static void Postfix(ref int __result)
    {
        __result *= 2;
    }
}

Здесь у нас есть префиксный патч, который вставляется перед запуском исходного метода, что позволяет нам устанавливать переменные внутри метода, устанавливать поля в классе метода или даже полностью пропускать исходный метод. У нас также есть постфиксный патч, который вставляется после запуска исходного метода и может управлять такими вещами, как возвращаемое значение.

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

person YellowApple    schedule 19.02.2021