如何知道何时在我的应用程序代码中调用任何方法
本文关键字:调用 任何 方法 代码 应用程序 何知道 何时 我的 | 更新日期: 2023-09-27 17:56:34
只是为了好玩,我想写一个方面,比如说日志记录、跟踪或检测/分析。但我不想使用任何已经可用的 AOP 框架。
我过去使用过PostSharp,现在 ASP.NET MVC引入了操作过滤器,这与方面/建议注入非常相似,.NET 4具有CodeContracts,它也与方面非常相似,我很清楚我希望我的方面API看起来像什么。
我唯一还没有弄清楚的,我相信它是构建 AOP 库的核心,是如何知道何时调用方法?
什么,我一直在监视调用堆栈吗?我是否查找主 UI 线程及其同步上下文以查看是否已为其分配了一些工作,即是否为其分配了要进入的方法?知道是否要调用方法的方法是什么?
有两种方法可以知道是否要调用方法。
-
您可以从 ContextBoundObject 继承,但您将失去基类的唯一机会。你可以检查这个
-
您可以继承您的类并重写您的方法。新方法可以在调用基方法之前调用侦听器。幸运的是,可以在运行时构造此派生类。实际上,这就是Castle的DynamicProxy所做的。具体而言,您将生成一个类型,该类型继承要在运行时截获的类,并实例化此派生类。将 IL 代码发出到新生成的类的方法中。
我不知道是否允许粘贴长代码,但这是我编写的DynamicProxy的小版本。它唯一依赖的库是 System
.你可以像这样使用它。
// typeof obj: public class TestClassProxy : TestClass, IInterface1, IIterface2
var obj = Proxy.Of<TestClass>(new CallHandler(), typeof(IInterface1), typeof(IInterface2));
obj.MethodVoid();
Console.WriteLine(obj.PublicSquare(3));
Console.WriteLine(obj.MethodString());
触发处理程序方法:
- 调用方法之前(BeforeMethodCall)
- 调用方法后(AfterMethodCall)
- 如果方法中发生异常(错误)
所有这些方法都接受参数、具有方法的对象、当前方法的 MethodInfo 以及传递给方法的参数。另外,AfterMethodCall接受返回值,OnError接受抛出的异常。
这是魔术Proxy
类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
namespace emit
{
public class Proxy
{
#region static
private static readonly AssemblyBuilder AssemblyBuilder;
private static readonly ModuleBuilder ModuleBuilder;
private static readonly object LockObj = new Object();
private static readonly Dictionary<string, Type> TypeCache = new Dictionary<string, Type>();
static Proxy()
{
lock (LockObj)
{
AssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
new AssemblyName("Taga.Proxies"),
AssemblyBuilderAccess.Run);
var assemblyName = AssemblyBuilder.GetName().Name;
ModuleBuilder = AssemblyBuilder.DefineDynamicModule(assemblyName);
}
}
private static Type GetImplementedType(Type baseType, Type[] interfaceTypes)
{
var key = GetTypeKey(baseType, interfaceTypes);
return TypeCache.ContainsKey(key) ? TypeCache[key] : null;
}
private static void AddImplementation(Type baseType, Type[] interfaceTypes, Type implementationType)
{
var key = GetTypeKey(baseType, interfaceTypes);
TypeCache.Add(key, implementationType);
}
private static string GetTypeKey(Type baseType, Type[] interfaceTypes)
{
var key = String.Empty;
key += baseType.FullName;
key = interfaceTypes.Aggregate(key, (current, interfaceType) => current + interfaceType);
return key;
}
public static TBase Of<TBase>(ICallHandler callHandler, params Type[] interfaceTypes) where TBase : class
{
var builder = new Proxy(typeof(TBase), interfaceTypes);
var type = builder.GetProxyType();
return (TBase)Activator.CreateInstance(type, callHandler);
}
public static object Of(ICallHandler callHandler, Type[] interfaceTypes)
{
if (interfaceTypes == null || interfaceTypes.Length == 0)
throw new InvalidOperationException("No interface type specified");
return Of<object>(callHandler, interfaceTypes);
}
#endregion
#region Proxy
private TypeBuilder _typeBuilder;
private FieldBuilder _callHandlerFieldBuilder;
private readonly Type _baseClassType;
private readonly Type[] _interfaceTypes;
private Proxy(Type baseClassType, Type[] interfaceTypes)
{
if (interfaceTypes == null || !interfaceTypes.Any())
_interfaceTypes = Type.EmptyTypes;
else if (interfaceTypes.Any(it => !it.IsInterface || !it.IsPublic || it.IsGenericType))
throw new InvalidOperationException("Interface Types must be public and non generic");
else
_interfaceTypes = interfaceTypes;
if (baseClassType == null)
_baseClassType = typeof(object);
else if (!baseClassType.IsClass || baseClassType.IsAbstract || baseClassType.IsGenericType || baseClassType.IsSealed || !baseClassType.IsPublic || !baseClassType.HasDefaultConstructor())
throw new InvalidOperationException("Base Class Type must be a public, non-sealed, non-abstract, non-generic class with a public default constructor");
else
_baseClassType = baseClassType;
}
private string _typeName;
private string TypeName
{
get { return _typeName ?? (_typeName = BuildTypeName()); }
}
private string BuildTypeName()
{
var typeName = "__";
if (_baseClassType != null)
typeName += _baseClassType.Name + "__";
foreach (var interfaceType in _interfaceTypes)
typeName += interfaceType.Name + "__";
return typeName + "Proxy__";
}
private Type GetProxyType()
{
var type = GetImplementedType(_baseClassType, _interfaceTypes);
if (type != null)
return type;
type = BuildType();
AddImplementation(_baseClassType, _interfaceTypes, type);
return type;
}
private Type BuildType()
{
InitTypeBuilder();
DefineCallHandlerField();
BuildConstructor();
ExtendBase();
ImplementInterfaces();
return _typeBuilder.CreateType();
}
private void InitTypeBuilder()
{
// public class __BaseClass__Interface1__Interface2__Proxy__ : BaseClass, Interface1, Interface2
_typeBuilder = ModuleBuilder.DefineType(
TypeName,
TypeAttributes.Public | TypeAttributes.Class,
_baseClassType,
_interfaceTypes);
}
private void DefineCallHandlerField()
{
// private ICallHandler _callHandler;
_callHandlerFieldBuilder = _typeBuilder.DefineField("_callHandler", typeof(ICallHandler), FieldAttributes.Private);
}
private void BuildConstructor()
{
var constructorBuilder = DeclareContsructor(); // public ProxyClass(ICallHandler callHandler)
ImplementConstructor(constructorBuilder); // : base() { this._callHandler = callHandler; }
}
private void ExtendBase()
{
foreach (var mi in _baseClassType.GetVirtualMethods())
BuildMethod(mi);
}
private void ImplementInterfaces()
{
foreach (var methodInfo in _interfaceTypes.SelectMany(interfaceType => interfaceType.GetMethods()))
BuildMethod(methodInfo);
}
private ConstructorBuilder DeclareContsructor()
{
var constructorBuilder = _typeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.HasThis,
new[] { typeof(ICallHandler) });
return constructorBuilder;
}
private void ImplementConstructor(ConstructorBuilder constructorBuilder)
{
var baseCtor = _baseClassType.GetConstructor(Type.EmptyTypes);
var il = constructorBuilder.GetILGenerator();
// call base ctor
il.Emit(OpCodes.Ldarg_0); // push this
il.Emit(OpCodes.Call, baseCtor); // Call base constructor this.base(); pops this
// set _callHandler
il.Emit(OpCodes.Ldarg_0); // push this
il.Emit(OpCodes.Ldarg_1); // push callHandler argument
il.Emit(OpCodes.Stfld, _callHandlerFieldBuilder); // this._callHandler = callHandler, pop this, pop callhandler argument
il.Emit(OpCodes.Ret); // exit ctor
}
private void BuildMethod(MethodInfo mi)
{
var methodBuilder = CallHandlerMethodBuilder.GetInstance(_typeBuilder, mi, _callHandlerFieldBuilder);
methodBuilder.Build();
}
#endregion
}
class CallHandlerMethodImplementor : CallHandlerMethodBuilder
{
internal CallHandlerMethodImplementor(TypeBuilder typeBuilder, MethodInfo methodInfo, FieldBuilder callHandlerFieldBuilder)
: base(typeBuilder, methodInfo, callHandlerFieldBuilder)
{
}
protected override void SetReturnValue()
{
// object res = returnValue;
ReturnValue = IL.DeclareLocal(typeof(object));
if (MethodInfo.ReturnType != typeof(void))
IL.Emit(OpCodes.Stloc, ReturnValue); // pop return value of BeforeCall into res
else
IL.Emit(OpCodes.Pop); // pop return value of BeforeCall
}
}
class CallHandlerMethodOverrider : CallHandlerMethodBuilder
{
internal CallHandlerMethodOverrider(TypeBuilder typeBuilder, MethodInfo methodInfo, FieldBuilder callHandlerFieldBuilder)
: base(typeBuilder, methodInfo, callHandlerFieldBuilder)
{
}
protected override void SetReturnValue()
{
// ReturnValue = base.Method(args...)
CallBaseMethod();
// stack'ta base'den dönen değer var
SetReturnValueFromBase();
}
private void CallBaseMethod()
{
IL.Emit(OpCodes.Pop); // pop return value of BeforeCall
// base'den Method'u çağır
// returnValue = base.Method(params...)
IL.Emit(OpCodes.Ldarg_0); // push this
for (var i = 0; i < ParameterCount; i++) // metoda gelen parametreleri stack'e at
IL.Emit(OpCodes.Ldarg_S, i + 1);// push params[i]
IL.Emit(OpCodes.Call, MethodInfo); // base.Method(params) pop this, pop params push return value
}
private void SetReturnValueFromBase()
{
ReturnValue = IL.DeclareLocal(typeof(object));
if (MethodInfo.ReturnType == typeof(void))
return;
// unbox returnValue if required
if (MethodInfo.ReturnType.IsValueType)
IL.Emit(OpCodes.Box, MethodInfo.ReturnType);
IL.Emit(OpCodes.Stloc, ReturnValue); // pop return value into res
}
}
abstract class CallHandlerMethodBuilder
{
private ParameterInfo[] _parameters;
private MethodBuilder _methodBuilder;
private readonly TypeBuilder _typeBuilder;
private readonly FieldBuilder _callHandlerFieldBuilder;
protected readonly MethodInfo MethodInfo;
protected ILGenerator IL { get; private set; }
protected int ParameterCount { get; private set; }
private MethodInfo _beforeCall;
private MethodInfo BeforeCall
{
get
{
return _beforeCall ?? (_beforeCall = typeof(ICallHandler).GetMethods().First(m => m.Name == "BeforeMethodCall"));
}
}
private MethodInfo _afterCall;
private MethodInfo AfterCall
{
get
{
return _afterCall ?? (_afterCall = typeof(ICallHandler).GetMethods().First(m => m.Name == "AfterMethodCall"));
}
}
private MethodInfo _onError;
private MethodInfo OnError
{
get
{
return _onError ?? (_onError = typeof(ICallHandler).GetMethods().First(m => m.Name == "OnError"));
}
}
protected CallHandlerMethodBuilder(TypeBuilder typeBuilder, MethodInfo methodInfo, FieldBuilder callHandlerFieldBuilder)
{
_typeBuilder = typeBuilder;
MethodInfo = methodInfo;
_callHandlerFieldBuilder = callHandlerFieldBuilder;
}
private void Declare()
{
// public override? ReturnType Method(arguments...)
_methodBuilder = _typeBuilder.DefineMethod(MethodInfo.Name,
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
MethodInfo.ReturnType,
MethodInfo.GetParameterTypes());
IL = _methodBuilder.GetILGenerator();
_parameters = MethodInfo.GetParameters();
ParameterCount = _parameters.Length;
}
private LocalBuilder _objParameter;
private void SetObjectParameter()
{
// CallHandlera verilecek object obj
_objParameter = IL.DeclareLocal(typeof(object)); // object obj;
IL.Emit(OpCodes.Ldarg_0); // push this
IL.Emit(OpCodes.Stloc, _objParameter); // obj = this; pops this
}
private LocalBuilder _methodInfoParameter;
private void SetMethodInfoParameter()
{
// CallHandlera verilecek MethodInfo methodInfo
_methodInfoParameter = IL.DeclareLocal(typeof(MethodInfo)); // MethodInfo methodInfo;
IL.Emit(OpCodes.Ldtoken, MethodInfo);
IL.Emit(OpCodes.Call, typeof(MethodBase).GetMethod(
"GetMethodFromHandle", new[] { typeof(RuntimeMethodHandle) })); // MethodBase.GetMethodFromHandle(new RuntimeMethodHandle());
IL.Emit(OpCodes.Stloc, _methodInfoParameter);
}
private LocalBuilder _argsParameter;
private void SetArgsParameters()
{
// CallHandlera verilecek object[] args
_argsParameter = IL.DeclareLocal(typeof(object[])); // object[] args;
IL.Emit(OpCodes.Ldc_I4, ParameterCount); // push parameterCount as Int32
IL.Emit(OpCodes.Newarr, typeof(object)); // push new object[parameterCount]; pops parameterCount
IL.Emit(OpCodes.Stloc, _argsParameter); // args = new object[ParameterCount]; pops new object[parameterCount]
// Metoda gelen parametreleri args'a doldur
for (var i = 0; i < ParameterCount; i++)
{
var parameterInfo = _parameters[i];
IL.Emit(OpCodes.Ldloc, _argsParameter); // push args
IL.Emit(OpCodes.Ldc_I4, i); // push i
IL.Emit(OpCodes.Ldarg_S, i + 1); // push params[i]; pops i; metoda gelen parametrelerin i'incisi. 0'ıncı parametre this olduğu için "+1" var
if (parameterInfo.ParameterType.IsPrimitive || parameterInfo.ParameterType.IsValueType)
IL.Emit(OpCodes.Box, parameterInfo.ParameterType); // (object)params[i]
IL.Emit(OpCodes.Stelem_Ref); // args[i] = (object)params[i]; pops params[i]
}
}
private void Try()
{
IL.BeginExceptionBlock(); // try {
}
private void InvokeBeforeMethodCall()
{
// this._callHandler.BeforeCall(obj, methodInfo, args);
IL.Emit(OpCodes.Ldarg_0); // push this
IL.Emit(OpCodes.Ldfld, _callHandlerFieldBuilder); // push _callHandler; pops this
IL.Emit(OpCodes.Ldloc, _objParameter); // push obj
IL.Emit(OpCodes.Ldloc, _methodInfoParameter); // push methodInfo
IL.Emit(OpCodes.Ldloc, _argsParameter); // push args
IL.Emit(OpCodes.Call, BeforeCall); // _callHandler.BeforeCall(obj, methodInfo, args); push return value
}
protected LocalBuilder ReturnValue;
protected abstract void SetReturnValue();
private void InvokeAfterMethodCall()
{
// this._callHandler.AfterCall(obj, methodInfo, args, returnValue);
IL.Emit(OpCodes.Ldarg_0); // push this
IL.Emit(OpCodes.Ldfld, _callHandlerFieldBuilder); // push _callHandler; pops this
IL.Emit(OpCodes.Ldloc, _objParameter); // push obj
IL.Emit(OpCodes.Ldloc, _methodInfoParameter); // push methodInfo
IL.Emit(OpCodes.Ldloc, _argsParameter); // push args
IL.Emit(OpCodes.Ldloc, ReturnValue); // push res
IL.Emit(OpCodes.Call, AfterCall); // _callHandler.AfterCall(obj, methodInfo, args, returnValue); push return value (void değilse)
}
private void Catch()
{
var ex = IL.DeclareLocal(typeof(Exception));
IL.BeginCatchBlock(typeof(Exception)); // catch
IL.Emit(OpCodes.Stloc_S, ex); // (Exception ex) {
InvokeOnError(ex); // _callHandler.AfterCall(obj, methodInfo, args);
IL.EndExceptionBlock(); // }
}
private void InvokeOnError(LocalBuilder exception)
{
// this._callHandler.OnError(obj, methodInfo, args);
IL.Emit(OpCodes.Ldarg_0); // push this
IL.Emit(OpCodes.Ldfld, _callHandlerFieldBuilder); // push _callHandler; pops this
IL.Emit(OpCodes.Ldloc, _objParameter); // push obj
IL.Emit(OpCodes.Ldloc, _methodInfoParameter); // push methodInfo
IL.Emit(OpCodes.Ldloc, _argsParameter); // push args
IL.Emit(OpCodes.Ldloc, exception); // push ex
IL.Emit(OpCodes.Call, OnError); // _callHandler.AfterCall(obj, methodInfo, args);
}
private void Return()
{
if (MethodInfo.ReturnType != typeof(void))
{
IL.Emit(OpCodes.Ldloc, ReturnValue); // push returnValue
IL.Emit(OpCodes.Unbox_Any, MethodInfo.ReturnType); // (ReturnType)returnValue
}
IL.Emit(OpCodes.Ret); // returns the value on the stack, if ReturnType is void stack should be empty
}
internal void Build()
{
Declare(); // public override? ReturnType Method(arguments...) {
SetObjectParameter(); // object obj = this;
SetMethodInfoParameter(); // MethodInfo methodInfo = MethodBase.GetMethodFromHandle(new RuntimeMethodHandle());
SetArgsParameters(); // object[] args = arguments;
Try(); // try {
InvokeBeforeMethodCall(); // object returnValue = _callHandler.BeforeMethodCall(obj, methodInfo, args);
SetReturnValue(); // !IsAbstract => returnValue = (object)base.Method(arguments);
InvokeAfterMethodCall(); // _callHandler.AfterMethodCall(obj, methodInfo, args, returnValue);
Catch(); // } catch (Exception ex) { _callHandler.OnError(obj, methodInfo, args, ex); }
Return(); // IsVoid ? (return;) : return (ReturnType)returnValue; }
}
internal static CallHandlerMethodBuilder GetInstance(TypeBuilder typeBuilder, MethodInfo methodInfo, FieldBuilder callHandlerFieldBuilder)
{
if (methodInfo.IsAbstract)
return new CallHandlerMethodImplementor(typeBuilder, methodInfo, callHandlerFieldBuilder);
return new CallHandlerMethodOverrider(typeBuilder, methodInfo, callHandlerFieldBuilder);
}
}
public interface ICallHandler
{
object BeforeMethodCall (object obj, MethodInfo mi, object[] args);
void AfterMethodCall (object obj, MethodInfo mi, object[] args, object returnValue);
void OnError (object obj, MethodInfo mi, object[] args, Exception exception);
}
static class ReflectionExtensions
{
public static bool HasDefaultConstructor(this Type type)
{
return type.GetConstructors(BindingFlags.Public | BindingFlags.Instance).Any(ctor => !ctor.GetParameters().Any());
}
public static Type[] GetParameterTypes(this MethodInfo methodInfo)
{
return methodInfo.GetParameters().Select(pi => pi.ParameterType).ToArray();
}
public static MethodBuilder GetMethodBuilder(this TypeBuilder typeBuilder, MethodInfo mi)
{
// MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual
return typeBuilder.DefineMethod(mi.Name, mi.Attributes, mi.ReturnType, mi.GetParameterTypes());
}
public static MethodInfo[] GetVirtualMethods(this Type type)
{
return type.GetMethods().Where(mi => mi.IsVirtual).ToArray();
}
public static object GetDefaultValue(this Type t)
{
return typeof(ReflectionExtensions).GetMethod("Default").MakeGenericMethod(t).Invoke(null, null);
}
public static T Default<T>()
{
return default(T);
}
}
}
这是测试代码
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Threading;
namespace emit
{
public class Program
{
private static void Main()
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-GB");
try
{
TestProxy();
Console.WriteLine("OK");
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.ReadLine();
}
private static void TestProxy()
{
var obj = Proxy.Of<TestClass>(new CallHandler(), typeof(IInterface1), typeof(IInterface2));
obj.MethodVoid();
Console.WriteLine();
Console.WriteLine(obj.PublicSquare(3));
Console.WriteLine();
Console.WriteLine(obj.MethodString());
Console.WriteLine();
Console.WriteLine(obj.MethodInt());
Console.WriteLine();
Console.WriteLine(obj.MethodComplex(45, " Deneme ", new Ogrenci { Name = "Ali" }).Name);
Console.WriteLine();
obj.PropInt = 78;
Console.WriteLine();
Console.WriteLine(obj.PropInt);
Console.WriteLine();
var int1 = obj as IInterface1;
int1.Name = "Interface";
Console.WriteLine();
Console.WriteLine("Got: " + int1.Name);
Console.WriteLine();
int1.Name = "Interface333";
Console.WriteLine();
Console.WriteLine("Got3: " + int1.Name);
Console.WriteLine();
Console.WriteLine(int1.MethodString(34, "Par", new Ogrenci { Name = "Veli" }));
var int2 = obj as IInterface2;
int2.Value = 14;
Console.WriteLine();
Console.WriteLine("Got: " + int2.Value);
Console.WriteLine();
int2.Value = 333;
Console.WriteLine();
Console.WriteLine("Got3: " + int2.Value);
Console.WriteLine();
Console.WriteLine(int2.MethodInt(34, "Par", new Ogrenci { Name = "Veli" }));
Console.WriteLine();
obj.ThrowException();
}
}
public class CallHandler : ICallHandler
{
private readonly SortedDictionary<string, object> _propertyValues = new SortedDictionary<string, object>();
public object BeforeMethodCall(object obj, MethodInfo mi, object[] args)
{
WriteParameterInfo(mi, args);
return SetReturnValue(mi, args);
}
public void AfterMethodCall(object obj, MethodInfo mi, object[] args, object returnValue)
{
if (mi.ReturnType == typeof(void))
Console.WriteLine(mi.Name + " returns [void]");
else
Console.WriteLine(mi.Name + " returns [" + (returnValue ?? "null") + "]");
}
public void OnError(object obj, MethodInfo mi, object[] args, Exception exception)
{
Console.WriteLine("Exception Handled: " + exception.Message);
throw new ApplicationException(exception.Message, exception);
}
private object SetReturnValue(MethodInfo mi, object[] args)
{
object res = null;
if (mi.Name.StartsWith("get_"))
{
var propName = mi.Name.Replace("get_", "");
if (_propertyValues.ContainsKey(propName))
res = _propertyValues[propName];
}
else if (mi.Name.StartsWith("set_"))
{
var propName = mi.Name.Replace("set_", "");
if (!_propertyValues.ContainsKey(propName))
_propertyValues.Add(propName, args[0]);
else
_propertyValues[propName] = args[0];
}
else if (mi.IsAbstract && mi.ReturnType != typeof(void))
{
res = mi.ReturnType.GetDefaultValue();
var methodName = mi.Name;
if (!_propertyValues.ContainsKey(methodName))
_propertyValues.Add(methodName, res);
else
_propertyValues[methodName] = res;
}
if (mi.ReturnType == typeof(void))
Console.WriteLine(mi.Name + " should return [void]");
else
Console.WriteLine(mi.Name + " should return [" + res + "]");
return res;
}
private void WriteParameterInfo(MethodInfo mi, object[] args)
{
Console.Write(mi.Name + " takes ");
if (args.Length == 0)
{
Console.WriteLine("no parameter");
}
else
{
Console.WriteLine("{0} parameter(s)", args.Length);
var paramInfos = mi.GetParameters();
for (int i = 0; i < args.Length; i++)
{
Console.WriteLine("'t[{0} {1}: '{2}']", paramInfos[i].ParameterType.Name, paramInfos[i].Name, args[i]);
}
}
}
}
public interface IInterface1
{
string Name { get; set; }
string MethodString(int i, string s, object o);
}
public interface IInterface2
{
int Value { get; set; }
int MethodInt(int i, string s, Ogrenci o);
}
public class TestClass
{
public virtual int PropInt { get; set; }
public virtual void ThrowException()
{
throw new Exception("Custom Error");
}
protected virtual double ProtectedSquare(int x)
{
Console.WriteLine("Executing Method ProtectedSquare");
return x * x;
}
public virtual double PublicSquare(int x)
{
Console.WriteLine("Executing Method PublicSquare");
return ProtectedSquare(x);
}
public virtual string MethodString()
{
Console.WriteLine("Executing String Method");
return "Hele";
}
public virtual int MethodInt()
{
Console.WriteLine("Executing Int Method");
return 985;
}
public virtual void MethodVoid()
{
Console.WriteLine("Executing Void Method");
}
public virtual Ogrenci MethodComplex(int x, string f, Ogrenci o)
{
Console.WriteLine("Executing Parameter Method");
return new Ogrenci { Name = o.Name + x + f };
}
}
public class Ogrenci
{
public string Name { get; set; }
public override string ToString()
{
return Name;
}
}
}
放置一个断点和你想查看它是否被调用的方法
如果每次都只放置一个断点是不够的:
正如您所说,观看调用堆栈。只需在认为合适时放置断点即可。
在Visual Studio中,这很容易......只需运行程序,当您想要断点时,将其设置在调试过程中,您知道这将要发生的行。
第三个选项是调用 Console.WriteLine
MSDN,即使它不是控制台应用程序。你将在输出窗口中看到结果(Ctrl + W,O)。
还可以使用 System.Diagnostics.Trace
MSDN 在"输出"窗口中进行写入。
以上技巧也不错,
你也可以在你的方法中放置JavaScript的警报框。 所以你会知道调用的方法。
如果要维护日志,可以在数据库中使用日志表,并将插入查询与日志时间列中的系统时间放在日志表中。 因此,通过此操作,您现在将在过去调用方法时调用。