Как зайти в Даркнет?!
25th January, 01:11
6
0
Как в tkinter из поля ввода Entry получить значение в одну переменную и обновить строку кнопкой, затем получить ещё одно введённое значение и затем сложить их. Ниже пример кода
21st July, 19:00
895
0
Программа, которая создает фейковые сервера в поиске игровых серверов CS 1.6 Steam
21st March, 17:43
948
0
Очень долго работает Update запрос Oracle
27th January, 09:58
914
0
не могу запустить сервер на tomcat HTTP Status 404 – Not Found
21st January, 18:02
906
0
Где можно найти фрилансера для выполнения поступающих задач, на постоянной основе?
2nd December, 09:48
938
0
Разработка мобильной кроссплатформенной военной игры
16th July, 17:57
1724
0
период по дням
25th October, 10:44
3955
0
Пишу скрипты для BAS только на запросах
16th September, 02:42
3720
0
Некорректный скрипт для закрытия блока
14th April, 18:33
4613
0
прокидывать exception в блоках try-catch JAVA
11th March, 21:11
4381
0
Помогите пожалуйста решить задачи
24th November, 23:53
6086
0
Не понимаю почему не открывается детальное описание продукта
11th November, 11:51
4351
0
Нужно решить задачу по программированию на массивы
27th October, 18:01
4396
0
Метода Крамера С++
23rd October, 11:55
4309
0
помогите решить задачу на C++
22nd October, 17:31
4002
0
Помогите решить задачу на python с codeforces
22nd October, 11:11
4492
0
Python с нуля: полное руководство для начинающих
18th June, 13:58
2599
0
Как перехватить вызов метода в C#?
Для данного класса я хотел бы иметь функцию трассировки, т. е. я хотел бы регистрировать каждый вызов метода (подпись метода и фактические значения параметров) и каждый выход метода (только подпись метода).
Как я могу это сделать предполагая что:
- Я не хочу использовать какую-либо третью сторону AOP библиотеки для C#,
- Я не хочу добавлять дубликат кода ко всем методам, которые я хочу trace,
- Я не хочу изменять public API класса - пользователи класса должны иметь возможность вызывать все методы точно таким же образом.
Чтобы сделать вопрос более конкретным предположим что есть 3 класса:
public class Caller
{
public static void Call()
{
Traced traced = new Traced();
traced.Method1();
traced.Method2();
}
}
public class Traced
{
public void Method1(String name, Int32 value) { }
public void Method2(Object object) { }
}
public class Logger
{
public static void LogStart(MethodInfo method, Object[] parameterValues);
public static void LogEnd(MethodInfo method);
}
Как вызвать Logger.LogStart и Logger.LogEnd для каждого вызова Method1 и Method2 без изменения метода Caller.Call и без добавления вызовов явно в Traced.Method1 и Traced.Method2 ?
Edit: каким будет решение, если мне будет разрешено немного изменить метод вызова?
C#-это не AOP-ориентированный язык. Он имеет некоторые функции AOP, и вы можете эмулировать некоторые другие, но создание AOP с C# болезненно.
Я искал способы сделать именно то, что вы хотели сделать, и не нашел простого способа сделать это.
Насколько я понимаю, это то, что вы хотите сделать:
[Log()]
public void Method1(String name, Int32 value);
и для этого у вас есть два основных варианта
Унаследуйте свой класс от MarshalByRefObject или ContextBoundObject и определите атрибут, который наследуется от IMessageSink. В этой статье есть хороший пример. Вы должны учитывать, что при использовании MarshalByRefObject производительность будет падать как ад, и я имею в виду это, я говорю о потере производительности в 10 раз, поэтому тщательно подумайте, прежде чем пытаться это сделать.
Другой вариант-ввести код напрямую. Во время выполнения, то есть вам придется использовать отражение для "read" каждого класса, получить его атрибуты и ввести соответствующий вызов (и в этом случае я думаю, что вы не могли бы использовать метод Reflection.Emit, поскольку я думаю, что Reflection.Emit не позволит вам вставить новый код в уже существующий метод). Во время разработки это будет означать создание расширения для компилятора CLR, о котором я, честно говоря, понятия не имею, как это делается.
Последний вариант-использование фреймворка IoC . Возможно, это не идеальное решение, поскольку большинство фреймворков IoC работает путем определения точек входа, которые позволяют подключать методы, но в зависимости от того, чего вы хотите достичь, это может быть справедливым аппроксимацией.
Самый простой способ достичь этого, вероятно, использовать PostSharp . Он вводит код внутри ваших методов на основе атрибутов, которые вы применяете к нему. Он позволяет вам делать именно то, что вы хотите.
Другой вариант-использовать профилирование API для внедрения кода внутри метода, но это действительно жесткий процесс.
Если вы напишете класс-назовем его Tracing-который реализует интерфейс IDisposable, вы можете обернуть все тела методов в
Using( Tracing tracing = new Tracing() ){ ... method body ...}
В классе трассировки вы можете обрабатывать логику трассировок в методе конструктора / Dispose, соответственно, в классе трассировки, чтобы отслеживать вход и выход методов. Такое, что:
public class Traced
{
public void Method1(String name, Int32 value) {
using(Tracing tracer = new Tracing())
{
[... method body ...]
}
}
public void Method2(Object object) {
using(Tracing tracer = new Tracing())
{
[... method body ...]
}
}
}
Вы можете достичь этого с помощью функции перехвата контейнера DI, такого как замок Виндзор . Действительно, можно настроить контейнер таким образом, чтобы все классы, имеющие метод, оформленный определенным атрибутом, были бы перехвачены.
Что касается пункта #3, OP, то он запросил решение без AOP рамок. В следующем ответе я предположил, что следует избегать таких вещей, как аспект, JointPoint, PointCut и т. д. Согласно документации по перехвату от CastleWindsor, ни один из них не требуется для выполнения того, что требуется.
Настройка универсальной регистрации перехватчика на основе наличия атрибута:
public class RequireInterception : IContributeComponentModelConstruction
{
public void ProcessModel(IKernel kernel, ComponentModel model)
{
if (HasAMethodDecoratedByLoggingAttribute(model.Implementation))
{
model.Interceptors.Add(new InterceptorReference(typeof(ConsoleLoggingInterceptor)));
model.Interceptors.Add(new InterceptorReference(typeof(NLogInterceptor)));
}
}
private bool HasAMethodDecoratedByLoggingAttribute(Type implementation)
{
foreach (var memberInfo in implementation.GetMembers())
{
var attribute = memberInfo.GetCustomAttributes(typeof(LogAttribute)).FirstOrDefault() as LogAttribute;
if (attribute != null)
{
return true;
}
}
return false;
}
}
Добавьте созданный IContributeComponentModelConstruction в контейнер
container.Kernel.ComponentModelBuilder.AddContributor(new RequireInterception());
И вы можете делать все что угодно в самом перехватчике
public class ConsoleLoggingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.Writeline("Log before executing");
invocation.Proceed();
Console.Writeline("Log after executing");
}
}
Добавить атрибут входа в свой способ войти
public class Traced
{
[Log]
public void Method1(String name, Int32 value) { }
[Log]
public void Method2(Object object) { }
}
Обратите внимание, что некоторая обработка атрибута потребуется, если необходимо перехватить только какой-то метод класса. По умолчанию все открытые методы будут перехвачены.
Я нашел другой путь, который может быть проще...
Объявить метод InvokeMethod
[WebMethod]
public object InvokeMethod(string methodName, Dictionary<string, object> methodArguments)
{
try
{
string lowerMethodName = '_' + methodName.ToLowerInvariant();
List<object> tempParams = new List<object>();
foreach (MethodInfo methodInfo in serviceMethods.Where(methodInfo => methodInfo.Name.ToLowerInvariant() == lowerMethodName))
{
ParameterInfo[] parameters = methodInfo.GetParameters();
if (parameters.Length != methodArguments.Count()) continue;
else foreach (ParameterInfo parameter in parameters)
{
object argument = null;
if (methodArguments.TryGetValue(parameter.Name, out argument))
{
if (parameter.ParameterType.IsValueType)
{
System.ComponentModel.TypeConverter tc = System.ComponentModel.TypeDescriptor.GetConverter(parameter.ParameterType);
argument = tc.ConvertFrom(argument);
}
tempParams.Insert(parameter.Position, argument);
}
else goto ContinueLoop;
}
foreach (object attribute in methodInfo.GetCustomAttributes(true))
{
if (attribute is YourAttributeClass)
{
RequiresPermissionAttribute attrib = attribute as YourAttributeClass;
YourAttributeClass.YourMethod();//Mine throws an ex
}
}
return methodInfo.Invoke(this, tempParams.ToArray());
ContinueLoop:
continue;
}
return null;
}
catch
{
throw;
}
}
Затем я определяю свои методы следующим образом
[WebMethod]
public void BroadcastMessage(string Message)
{
//MessageBus.GetInstance().SendAll("<span class='system'>Web Service Broadcast: <b>" + Message + "</b></span>");
//return;
InvokeMethod("BroadcastMessage", new Dictionary<string, object>() { {"Message", Message} });
}
[RequiresPermission("editUser")]
void _BroadcastMessage(string Message)
{
MessageBus.GetInstance().SendAll("<span class='system'>Web Service Broadcast: <b>" + Message + "</b></span>");
return;
}
Теперь я могу иметь проверку во время выполнения без инъекции зависимостей...
Никаких готчей на сайте :)
Надеюсь, вы согласитесь, что это меньший вес, чем фреймворк AOP или производный от MarshalByRefObject или использующий удаленное взаимодействие или прокси-классы.
Взгляните на это - довольно тяжелая штука.. http://msdn.microsoft.com/en-us/magazine/cc164165.aspx
Важно .net - не поле была глава о том, что вам нужно называется Перехват. Я нацарапал некоторые из них здесь (Извините за цвет шрифта - у меня тогда была темная тема...) http://madcoderspeak.blogspot.com/2005/09/essential-interception-using-contexts.html
Сначала вы должны изменить свой класс, чтобы реализовать интерфейс (а не реализовать MarshalByRefObject).
interface ITraced {
void Method1();
void Method2()
}
class Traced: ITraced { .... }
Далее вам нужен универсальный объект-оболочка на основе RealProxy, чтобы украсить любой интерфейс, позволяющий перехватывать любой вызов к оформленному объекту.
class MethodLogInterceptor: RealProxy
{
public MethodLogInterceptor(Type interfaceType, object decorated)
: base(interfaceType)
{
_decorated = decorated;
}
public override IMessage Invoke(IMessage msg)
{
var methodCall = msg as IMethodCallMessage;
var methodInfo = methodCall.MethodBase;
Console.WriteLine("Precall " + methodInfo.Name);
var result = methodInfo.Invoke(_decorated, methodCall.InArgs);
Console.WriteLine("Postcall " + methodInfo.Name);
return new ReturnMessage(result, null, 0,
methodCall.LogicalCallContext, methodCall);
}
}
Теперь мы готовы перехватить вызовы Method1 и Method2 из ITraced
public class Caller
{
public static void Call()
{
ITraced traced = (ITraced)new MethodLogInterceptor(typeof(ITraced), new Traced()).GetTransparentProxy();
traced.Method1();
traced.Method2();
}
}
Если вы хотите trace после ваших методов без ограничений (без адаптации кода, без фреймворка AOP, без дубликата кода), позвольте мне сказать вам, что вам нужно немного магии...
Серьезно, я решил реализовать фреймворк AOP, работающий во время выполнения.
Вы можете найти здесь : NConcern .NET AOP Framework
Я решил создать этот фреймворк AOP, чтобы дать ответ на такого рода потребности. это простая библиотека, очень легкая. Вы можете увидеть пример logger на домашней странице.
Если вы не хотите использовать сторонний assembly, вы можете просмотреть исходный код (open source) и скопировать оба файла Aspect.Directory.cs и Aspect.Directory.Entry.cs в адаптированный по вашему желанию. Эти классы позволяют заменить ваши методы во время выполнения. Я бы просто попросил вас уважать лицензию.
Я надеюсь, что вы найдете то, что вам нужно, или убедите вас наконец использовать фреймворк AOP.
Вы можете использовать open source framework CInject на CodePlex. Вы можете написать минимальный код для создания инжектора и заставить его быстро перехватить любой код с помощью CInject. Кроме того, поскольку это открытый исходный код, вы также можете расширить его.
Или вы можете выполнить действия, описанные в этой статье о перехвате вызовов методов с помощью IL и создать свой собственный перехватчик с использованием Reflection.Emit классов в C#.
вам нужно вызвать айенде для ответа о том, как он это сделал: http://ayende.com/Blog/archive/2009/11/19/can-you-hack-this-out.aspx
AOP является обязательным для реализации чистого кода, однако если вы хотите окружить блок в C#, универсальные методы имеют относительно более простое использование. (с помощью intelli sense и строго типизированного кода) конечно, это может быть NOT альтернативой AOP.
Хотя у PostSHarp есть небольшие проблемы с багами (я не чувствую себя уверенно для использования в производстве), это хороший материал.
Оболочку универсального класса ,
public class Wrapper
{
public static Exception TryCatch(Action actionToWrap, Action<Exception> exceptionHandler = null)
{
Exception retval = null;
try
{
actionToWrap();
}
catch (Exception exception)
{
retval = exception;
if (exceptionHandler != null)
{
exceptionHandler(retval);
}
}
return retval;
}
public static Exception LogOnError(Action actionToWrap, string errorMessage = "", Action<Exception> afterExceptionHandled = null)
{
return Wrapper.TryCatch(actionToWrap, (e) =>
{
if (afterExceptionHandled != null)
{
afterExceptionHandled(e);
}
});
}
}
использование может быть таким (с intelli sense, конечно)
var exception = Wrapper.LogOnError(() =>
{
MessageBox.Show("test");
throw new Exception("test");
}, "Hata");
Я не знаю решения, но мой подход будет следующим.
Украсьте класс (или его методы) пользовательским атрибутом. В другом месте программы Пусть функция инициализации отражает все типы, считывает методы, украшенные атрибутами, и вводит в метод некоторый код IL. На самом деле было бы более практично заменить метод заглушкой , которая вызывает LogStart, фактический метод, а затем LogEnd . Кроме того, я не знаю, можно ли изменить методы с помощью отражения, поэтому было бы более практично заменить весь тип.
- Напишите свою собственную библиотеку AOP.
- Используйте reflection для создания прокси-сервера ведения журнала над вашими экземплярами (не уверен, что вы можете сделать это без изменения какой-то части существующего кода).
- Перепишите assembly и введите свой код регистрации (в основном тот же, что и 1).
- Разместите CLR и добавьте ведение журнала на этом уровне (я думаю, что это самое сложное решение для реализации, хотя не уверен, что у вас есть необходимые крючки в CLR).
Лучшее, что вы можете сделать до выпуска C# 6 с 'nameof', - это использовать медленные выражения StackTrace и linq.
E.g. для такого способа
public void MyMethod(int age, string name)
{
log.DebugTrace(() => age, () => name);
//do your stuff
}
Такая строка может быть создана в вашем лог-файле
Method 'MyMethod' parameters age: 20 name: Mike
Вот такая реализация:
//TODO: replace with 'nameof' in C# 6
public static void DebugTrace(this ILog log, params Expression<Func<object>>[] args)
{
#if DEBUG
var method = (new StackTrace()).GetFrame(1).GetMethod();
var parameters = new List<string>();
foreach(var arg in args)
{
MemberExpression memberExpression = null;
if (arg.Body is MemberExpression)
memberExpression = (MemberExpression)arg.Body;
if (arg.Body is UnaryExpression && ((UnaryExpression)arg.Body).Operand is MemberExpression)
memberExpression = (MemberExpression)((UnaryExpression)arg.Body).Operand;
parameters.Add(memberExpression == null ? "NA" : memberExpression.Member.Name + ": " + arg.Compile().DynamicInvoke().ToString());
}
log.Debug(string.Format("Method '{0}' parameters {1}", method.Name, string.Join(" ", parameters)));
#endif
}