Как перехватить вызов невиртуального метода из/в сторонние библиотеки в .Net?

Я думаю, что мне нужно то, что люди .net называют «прозрачным динамическим прокси», но все реализации, которые я видел до сих пор (Castle DynamicProxy, Spring.NET AOP и т. д.), требуют, чтобы я сделал хотя бы одно из этого:

  1. Объявить перехваченный метод виртуальным
  2. Оберните класс и создайте экземпляры оболочки вместо обернутого класса
  3. Изменить наследование или реализовать интерфейсы

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

Если бы C# был динамическим языком, таким как Python, я бы сделал что-то вроде этого:

foo = ThirdyPartyLibA.Foo()
def interceptor(self, *args, **kwargs):
    do_something_before(self, *args, **kwargs)
    result = ThirdyPartyLibB.Bar.intercepted(self, *args, **kwargs)
    do_something_after(self, result, *args, **kwargs)
    return result
foo.bar.intercepted = interceptor # bar is an instance of ThirdyPartyLibB.Bar
foo.do_its_job() # Foo.do_its_job calls Bar.intercepted

Мне нужно это, чтобы изменить плохое поведение ThirdyPartyLibA.Foo при взаимодействии с ThirdyPartyLibB.Bar. Я точно знаю, что вызывает такое поведение и как именно изменить Foo или Bar, чтобы исправить эту ошибку благодаря дизассемблерам.

Некоторые (очень маловероятные) идеи:

  • Разберите ThirdyPartyLibA, внесите изменения в код и сгенерируйте совместимую сборку (вряд ли это сработает, потому что это сборка со строгим именем)
  • Отредактируйте двоичный файл, чтобы сделать ошибочные методы Foo виртуальными, и измените все, что необходимо, чтобы он оставался допустимой сборкой, чтобы я мог использовать динамические прокси (очень маловероятно, что это сработает, также по той же причине, что и идея выше)
  • Найдите подходящую реализацию прозрачного динамического прокси-сервера (думаю, ее нет на основе этой ветки форума: http://www.pcreview.co.uk/forums/overriding-non-virtual-methods-using-il).-and-reflection-emit-t2605695.html)
  • Обратитесь в компанию, создавшую библиотеку (они больше не поддерживают продукт)
  • Прекратите использовать библиотеку или используйте альтернативу (невозможно, так как это часть среды выполнения RAD IDE, к которой мы привязаны, потому что существует ОГРОМНОЕ количество кода, написанного с использованием собственного языка IDE)
  • Контролируйте вызовы проблемных методов, чтобы избежать ошибки (мы уже сделали это, но это не решило проблему полностью)

У вас есть другая идея?

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


person ygormutti    schedule 01.08.2012    source источник
comment
Вы можете попробовать использовать методы расширения C # 3.5, синтаксис похож на тот, что используется в Phyton, они позволяют добавлять функциональность в запечатанные классы, например. создавайте методы экземпляров для классов, которыми вы не управляете (запечатанные, сторонние и т. д.). Единственное: вы не можете заменить или перезаписать метод, который уже реализован в классе.   -  person Adrian Salazar    schedule 01.08.2012
comment
Методы расширения не могут решить эту проблему, потому что, как вы знаете, они не могут переопределять методы, и я не могу изменить методы, которые библиотека A вызывает из библиотеки B.   -  person ygormutti    schedule 02.08.2012


Ответы (2)


Возможное решение 1:

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

Возможное решение 2:

Хотя TypeMock обычно используется в качестве инструмента тестирования, он позволит вам имитировать все. Поскольку он внедряет себя как профилировщик кода, то, что вы можете имитировать, включает частные и статические члены классов. В качестве бонуса любые переопределенные методы не обязательно должны быть виртуальными, поэтому вы можете перехватывать вызовы таким образом.

Моя рекомендация

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

person Josh Kodroff    schedule 01.08.2012
comment
+1 за все, кроме того, что я бы даже порекомендовал вам использовать сторонние библиотеки в качестве общего правила, что меня немного озадачивает. - person spender; 01.08.2012
comment
Я не могу заменить все случаи использования библиотеки в коде оболочкой, потому что не мой код использует библиотеку, а другая сторонняя библиотека. Я использую библиотеку A, которая неправильно использует библиотеку B. Я могу обернуть свое использование библиотеки A, но это не исправит ошибочное взаимодействие между A и B. - person ygormutti; 02.08.2012
comment
Второе решение кажется многообещающим. Я расследую это. - person ygormutti; 02.08.2012
comment
@spender Достаточно честно. Поясню: Всегда заворачивайте дерьмовую стороннюю библиотеку. Настоятельно рассмотрите возможность включения в оболочку любой другой сторонней библиотеки, которая, по вашему мнению, может быть изменена в ходе работы над приложением. В последнем случае речь идет о том, чтобы не раскидывать зависимости от библиотеки по всей кодовой базе. - person Josh Kodroff; 02.08.2012

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

person Dan Bryant    schedule 01.08.2012
comment
И Foo, и Bar не статичны. :/ Ваша ссылка кажется очень интересной. Я проверю это. (да, я в отчаянии) - person ygormutti; 01.08.2012
comment
Согласно странице Mole на сайте Microsoft Research, Fakes Framework [...] является следующим поколением Moles & Stubs и в конечном итоге заменит его. К сожалению, и Moles, и Fakes нуждаются в том, чтобы приложение размещалось на каком-то профилировщике. Я в отчаянии, но еще не настолько. ;) - person ygormutti; 15.08.2012