Могу ли я заменить метод С# на другое с тем же именем и подписью?

У меня есть следующая ситуация. Некоторое время работы .Net не работает очень хорошо, и мне нужно создать обходной путь. Как там SqlCommand.ExecuteReader(), который иногда возвращает закрытый объект считывателя, и я хочу иметь такой код:

 SqlDataReader MyExecuteReader( this SqlCommand command )
 {
     var reader = command.ExecuteReader();
     if( reader.IsClosed() ) {
        throw new ClosedReaderReturnedException();
     }
     return reader;
 }

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

Есть ли способ каким-то образом объявить, что когда какой-либо из моих кодов хочет SqlCommand.ExecuteReader(), называемый MyExecuteReader(), вызывается вместо этого? Эффективно ли можно заменить существующий метод другим, имеющим точно такую ​​же подпись и одно и то же имя?

Ответ 1

Это похоже на проблемы при попытке использования unit test кода с использованием mocks.

В одностороннем порядке заменить использование SqlCommand в коде объектом, реализующим интерфейс с помощью метода ExecuteReader на нем. Затем вы можете легко заменить объект, возможно, используя шаблон factory.

Итак, вы заменили бы код следующим образом:

using (SqlCommand command = new SqlCommand(query))
{
    command.ExecuteReader();
}

с:

var sqlCommandFactory = new SqlCommandFactory();
using (ISqlCommand command = sqlCommandFactory.CreateSqlCommand(query))
{
    command.ExecuteReader();
}

Сначала определим интерфейс, который содержит методы, которые вы хотите заменить:

public interface ISqlCommand
{
    SqlDataReader ExecuteReader();

    // further interface methods here...
}

Затем создайте factory, который использует ту же подпись, что и конструктор SqlCommand:

internal class SqlCommandFactory
{
    bool _useMyClass = true;

    public ISqlCommand CreateSqlCommand(string query)
    {
        if (_useMyClass)
        {
            return new MySqlCommand(query);
        }
        else
        {
            return new SqlCommandWrapper(query);
        }
    }
}

Затем вы записываете свой код-заменитель в класс MySqlCommand:

public MySqlCommand : ISqlCommand
{
    public SqlDataReader ExecuteReader()
    {
        // your new code here
    }
}

Поскольку класс .NET SqlCommand, очевидно, не реализует новый интерфейс ISqlCommand, создайте класс-оболочку, который делает следующее:

public SqlCommandWrapper : ISqlCommand
{
    SqlCommand _sqlCommand;

    public SqlCommandWrapper(string query)
    {
        _sqlCommand = new SqlCommand(query);
    }

    public SqlDataReader ExecuteReader()
    {
        _sqlCommand.ExecuteReader();
    }
}

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

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

Ответ 2

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

Ваш лучший вариант - это реализовать стандартный метод расширения с другим именем и заменить все обычаи. Это может показаться большой работой в большой базе кода и может быть подвержено человеческим ошибкам в будущем - кто-то добавляет новый вызов к исходному методу. Однако однократная стоимость компенсируется тем фактом, что ваш код теперь явно, что вы внесли изменения в поведение; и вы можете защитить от человеческих ошибок, написав свое собственное правило FxCop (или любой другой инструмент статического анализа, который вы используете регулярно).

Ответ 3

Ну, вы можете использовать такую ​​библиотеку, как Cecil, чтобы переписать IL, как это сделано здесь: http://plaureano.blogspot.dk/2011/05/introduction-to-il-rewriting-with-cecil.html?m=1

Но я верю, что лучше переписать свой код, так что кому-то (и самому себе позже) станет ясно, кто может читать ваш код, что происходит:)

Ответ 4

Я не думаю, что ты хочешь это сделать. Это будет путать всех, кто читает код.

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

По-видимому, кому-то удалось выяснить, как внедрить .NET-методы.

Имейте в виду, что это, вероятно, будет намного больше проблем, чем стоит. Вам придется QA широко использовать всевозможные платформы с использованием всех видов дополнительного программного обеспечения (один вид антивируса может сломать ваш код). Так что, просто сделайте то, что предложили все остальные, - создайте свой метод расширения и просто выполните поиск и замените всю вашу базу кода.