如何知道何时在我的应用程序代码中调用任何方法

本文关键字:调用 任何 方法 代码 应用程序 何知道 何时 我的 | 更新日期: 2023-09-27 17:56:34

只是为了好玩,我想写一个方面,比如说日志记录、跟踪或检测/分析。但我不想使用任何已经可用的 AOP 框架。

我过去使用过PostSharp,现在 ASP.NET MVC引入了操作过滤器,这与方面/建议注入非常相似,.NET 4具有CodeContracts,它也与方面非常相似,我很清楚我希望我的方面API看起来像什么。

我唯一还没有弄清楚的,我相信它是构建 AOP 库的核心,是如何知道何时调用方法?

什么,我一直在监视调用堆栈吗?我是否查找主 UI 线程及其同步上下文以查看是否已为其分配了一些工作,即是否为其分配了要进入的方法?知道是否要调用方法的方法是什么?

如何知道何时在我的应用程序代码中调用任何方法

有两种方法可以知道是否要调用方法。

  1. 您可以从 ContextBoundObject 继承,但您将失去基类的唯一机会。你可以检查这个

  2. 您可以继承您的类并重写您的方法。新方法可以在调用基方法之前调用侦听器。幸运的是,可以在运行时构造此派生类。实际上,这就是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());

触发处理程序方法:

  1. 调用方法之前(BeforeMethodCall)
  2. 调用方法后(AfterMethodCall)
  3. 如果方法中发生异常(错误)

所有这些方法都接受参数、具有方法的对象、当前方法的 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的警报框。 所以你会知道调用的方法。

如果要维护日志,可以在数据库中使用日志表,并将插入查询与日志时间列中的系统时间放在日志表中。 因此,通过此操作,您现在将在过去调用方法时调用。