Могу ли я заменить метод С# на другое с тем же именем и подписью?
У меня есть следующая ситуация. Некоторое время работы .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 широко использовать всевозможные платформы с использованием всех видов дополнительного программного обеспечения (один вид антивируса может сломать ваш код). Так что, просто сделайте то, что предложили все остальные, - создайте свой метод расширения и просто выполните поиск и замените всю вашу базу кода.