How to know when any method is called in my application code? - c#

Just for fun, I want to write an aspect such as say logging, tracing or instrumentation/profiling. But I don't want to use any of the available AOP frameworks already available.
I've used PostSharp in the past and now that ASP.NET MVC introduces action filters, which are very similar to aspect/advise injection, and .NET 4 has CodeContracts, which are also very similar to aspects, I have a pretty good idea of what I want my aspect API to look like.
The only thing I haven't figured out yet, and I believe that it is central to building an AOP library, is how to know when a method is being invoked?
What, do I keep watching the callstack? Do I look up the main UI thread and its synchronization context to see if it has been assigned some work, i.e. been given a method to enter into? What is the way to know if a method is going to be called?

There are two ways to know if a method is going to be called.
You can inherit from ContextBoundObject but you lose the only chance of base class. You can check this
You can inherit your class and override your methods. New methods can call a interceptor before calling base method. Fortunately, this derived class can be constructed in run time. Actually, this is what Castle's DynamicProxy does. In detail you build a type that inherits your class to be intercepted in run time and instantiate this derived class. You Emit IL code into methods of newly generated class.
I do not know if pasting long codes are allowed but here is a little version of DynamicProxy that I wrote. The only library it depends on is System. You can use it like this.
// 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());
A handler method is triggered:
Before calling a method (BeforeMethodCall)
After calling a method (AfterMethodCall)
If an exception occured in method (OnError)
All these methods accepts the parameters, object that has the method, MethodInfo of the current method and aguments passed to method. Addtionally AfterMethodCall takes return value and OnError takes the exception that has thrown.
Here is the magic Proxy class
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);
}
}
}
And here is the test code
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;
}
}
}

Put a breakpoint and the method that you want to see if it's getting called

If just placing a breakpoint every time does not suffice:
As you said, watch the Call Stack. Just place a breakpoint when you see fit.
In Visual Studio it's pretty easy.. just run the program and when you want a breakpoint, set it in the middle of debugging in a line you know that's about to happen.
Third option is to call Console.WriteLineMSDN even if it's not a console application.
You'll see the result in the Output Window (Ctrl + W, O) instead.
You can also use System.Diagnostics.TraceMSDN for writing in the Output window.

Above tricks are also good,
You can also put alert box of JavaScript in your method. so you will know that the method called.
If you want to maintain the log you can have log table in your database and put the insert query with the system time in log time column in your log table. so by this you will now when in past your method called.

Related

C#: Custom Delegate but the first parameter is given

I want to store callbacks for WebSocket calls from the clientside.
I want to tell the IDE the first param should always be ClientMetadata.
The problem is, that I want to register methods which would have infinite amount and unknown type of parameters but the first must be a ClientMetadata object.
I want to register the callbacks like this:
RegisterEventHandler("callback1", (ClientMetadata client, string smth1, int smth2);
RegisterEventHandler("callback2", (ClientMetadata client, bool smth3);
I have a method to store callbacks:
public static void RegisterEventHandler(string identifier, Delegate callback)
{
callbacks.Add(identifier, callback);
}
Here is where the callbacks are stored:
private static Dictionary<string, EventHandlerCallback> callbacks = new Dictionary<string, EventHandlerCallback>();
Here is how the stored methods get invoked:
private static void Server_MessageReceive2d(object? sender, MessageReceivedEventArgs e)
{
string jsonObj = Encoding.UTF8.GetString(e.Data);
if (jsonObj != null)
{
WSMessage obj = JsonConvert.DeserializeObject<WSMessage>(jsonObj);
if (obj == null || obj.message == null)
{
return;
}
//obj.identifier is the id of the callback in the serverside
if (callbacks.ContainsKey(obj.identifier))
{
MethodInfo mi = callbacks[obj.identifier].GetMethodInfo();
if (obj.callbackId != null && mi.ReturnType.ToString() != "System.Void")
{
//e.Client --> ClientMetaData
//obj.message --> dynamic[]?
var ret = callbacks[obj.identifier].DynamicInvoke(e.Client, obj.message);
//obj.callbackId is the callback to call when the method ended
if (ret != null)
{
SendMessage(e.Client, obj.callbackId, ret);
}
else
{
SendMessage(e.Client, obj.callbackId);
}
}
else
{
callbacks[obj.identifier].DynamicInvoke(e.Client, obj.message);
}
}
else
{
return;
}
}
}
I tried making a custom delegate:
delegate void EventHandlerCallback(ClientMetadata clientMetadata, params object[] data);
Turns out, the params keyword does absolutely nothing in this situation. Either ways I use params or not, my IDE expects this:
RegisterEventHandler("callback2", (ClientMetadata client, object[] smthObj);
You can create a delegate that takes a ClientMetadata object as the first argument and an array of objects as the second argument. This way, you can store callbacks with any number of unknown parameters:
delegate void EventHandlerCallback(ClientMetadata clientMetadata, object[] data);
public static void RegisterEventHandler(string identifier,
EventHandlerCallback callback)
{
callbacks.Add(identifier, callback);
}
// Usage:
RegisterEventHandler("callback1", (ClientMetadata client, object[] args) =>
{
string smth1 = (string)args[0];
int smth2 = (int)args[1];
});
RegisterEventHandler("callback2", (ClientMetadata client, object[] args) =>
{
bool smth3 = (bool)args[0];
});
You can go for Code Analyzer... Something like this should do the trick:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using System.Collections.Immutable;
namespace AnalyzerTest
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class AnalyzerTestAnalyzer : DiagnosticAnalyzer
{
public const string ExpectedParameterType = "ClientMetadata";
public const string MethodName = "RegisterEventHandler";
public const string DiagnosticId = "SEL001";
public const string Title = "AnalyzerTest";
public const string MessageFormat = "Method call '{0}' wrong argument. First parameter of delegate should be '{1}'";
public const string Category = "Error";
private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(NodeAction, new SyntaxKind[] { SyntaxKind.InvocationExpression });
}
private void NodeAction(SyntaxNodeAnalysisContext context)
{
var registerMethodSymbol = (IMethodSymbol)context.SemanticModel.GetSymbolInfo(context.Node).Symbol;
if(registerMethodSymbol.Name == MethodName)
{
IMethodSymbol methodSymbol;
var callbackSyntax = ((InvocationExpressionSyntax)context.Node).ArgumentList.Arguments[1];
var callbackTypeInfo = context.SemanticModel.GetTypeInfo(callbackSyntax.Expression);
if(callbackTypeInfo.Type != null)
{
var namedType = callbackTypeInfo.Type as INamedTypeSymbol;
//Action<ClientMetadata, ...> or Func<ClientMetadata, ..., ret> or delegate
methodSymbol = namedType.DelegateInvokeMethod;
}
else
{
//Method
var symbolInfo = context.SemanticModel.GetSymbolInfo(callbackSyntax.Expression);
methodSymbol = (symbolInfo.Symbol ?? (symbolInfo.CandidateSymbols != null ? symbolInfo.CandidateSymbols[0] : null)) as IMethodSymbol;
}
if(methodSymbol != null && methodSymbol.Parameters.Length > 0 && methodSymbol.Parameters[0].Type.Name == ExpectedParameterType)
{
return;
}
var diagnostic = Diagnostic.Create(Rule, callbackSyntax.GetLocation(), MethodName, ExpectedParameterType);
context.ReportDiagnostic(diagnostic);
}
}
}
}
Here is example how it works (of course it causing compile time error)

How to intercept an async method with IAsyncEnumerable<T> as return type in .netcore 3.0

Im trying to implement an proxy that takes the work of logging method calls. With awareness of async methods that should be called and some of the logs should only be made after the method finished but the proxy method should not be a blockig call.
After many tries this is the solution I came up with.
namespace ClassLibrary1
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Threading.Tasks;
public static class ObjectExtender
{
internal static bool IsOfGenericType(this object obj, Type check, out Type? genericType)
{
Type actType = obj.GetType();
while (actType != null && actType != typeof(object))
{
if (actType.IsGenericType && actType.GetGenericTypeDefinition() == check.GetGenericTypeDefinition())
{
genericType = actType;
return true;
}
actType = actType.BaseType;
}
genericType = null;
return false;
}
}
public class Class1<T> : DispatchProxy
{
private static readonly MethodInfo AsyncEnumeration;
private static readonly Dictionary<Type, MethodInfo> CachedAsyncEnumerationMethodInfos = new Dictionary<Type, MethodInfo>();
private static readonly Dictionary<Type, MethodInfo> CachedGenericTaskMethodInfos = new Dictionary<Type, MethodInfo>();
private static readonly Dictionary<Type, MethodInfo> CachedSyncEnumerationMethodInfos = new Dictionary<Type, MethodInfo>();
private static readonly MethodInfo GenericTask;
private static readonly MethodInfo SyncEnumeration;
private T _decorated = default!;
static Class1()
{
GenericTask = typeof(Class1<T>).GetMethod("HandleTaskGenericAsync", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
AsyncEnumeration = typeof(Class1<T>).GetMethod("Wrapper", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
SyncEnumeration = typeof(Class1<T>).GetMethod("SyncWrapper", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
}
public static T Create(T decorated)
{
T proxy = Create<T, Class1<T>>();
Class1<T> ap = proxy as Class1<T> ?? throw new ArgumentNullException(nameof(decorated));
ap._decorated = decorated;
return proxy;
}
private static Task<T2> HandleTaskGenericAsync<T1, T2>(T1 result, MethodInfo methodName) where T1 : Task<T2>
{
return result.ContinueWith(parent =>
{
Console.WriteLine($"After: {methodName}");
return parent.Result;
});
}
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
try
{
Console.WriteLine($"Before: {targetMethod}");
object result = targetMethod.Invoke(_decorated, args);
if (result is Task resultTask)
{
if (!resultTask.IsOfGenericType(typeof(Task<>), out Type? genericType))
{
return resultTask.ContinueWith(task =>
{
if (task.Exception != null)
{
Console.WriteLine($"{task.Exception.InnerException ?? task.Exception}, {targetMethod}");
}
else
{
Console.WriteLine($"After: {targetMethod}");
}
});
}
Debug.Assert(genericType != null, nameof(genericType) + " != null");
Type resultType = genericType.GetGenericArguments()[0]; // Task<> hat nur einen.
if (!CachedGenericTaskMethodInfos.ContainsKey(resultType))
{
CachedGenericTaskMethodInfos.Add(resultType, GenericTask.MakeGenericMethod(genericType, resultType));
}
return CachedGenericTaskMethodInfos[resultType].Invoke(null, new object[] {resultTask, targetMethod});
}
Type returnType = targetMethod.ReturnType;
if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(IAsyncEnumerable<>))
{
Type resultType = returnType.GetGenericArguments()[0]; //IAsyncEnumerable hat nur eines
if (!CachedAsyncEnumerationMethodInfos.ContainsKey(resultType))
{
CachedAsyncEnumerationMethodInfos.Add(resultType, AsyncEnumeration.MakeGenericMethod(resultType));
}
return CachedAsyncEnumerationMethodInfos[resultType].Invoke(null, new[] {result, targetMethod});
}
if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
{
Type resultType = returnType.GetGenericArguments()[0]; //IAsyncEnumerable hat nur eines
if (!CachedSyncEnumerationMethodInfos.ContainsKey(resultType))
{
CachedSyncEnumerationMethodInfos.Add(resultType, SyncEnumeration.MakeGenericMethod(resultType));
}
return CachedSyncEnumerationMethodInfos[resultType].Invoke(null, new[] {result, targetMethod});
}
Console.WriteLine($"After: {targetMethod}");
return result;
}
catch (TargetInvocationException ex)
{
Console.WriteLine($"{ex.InnerException ?? ex}, {targetMethod}");
throw;
}
}
private static IEnumerable<T> SyncWrapper<T>(IEnumerable<T> inner, MethodInfo targetMethod)
{
foreach (T t in inner)
{
yield return t;
}
Console.WriteLine($"After List: {targetMethod}");
}
private static async IAsyncEnumerable<T> Wrapper<T>(IAsyncEnumerable<T> inner, MethodInfo targetMethod)
{
await foreach (T t in inner)
{
yield return t;
}
Console.WriteLine($"After List: {targetMethod}");
}
}
}
This proxy intercepts method calls just the way I wanted to.
This is the output of my Test
---Test sync calls---
Before: Void Run()
Inside: Run()
After: Void Run()
Before: System.Collections.Generic.IEnumerable`1[System.Int32] RunEnumerator[Int32](Int32[])
Inside Start: RunEnumerator()
Erg: 1
Erg: 2
Erg: 3
Erg: 4
Inside Ende: RunEnumerator()
After List: System.Collections.Generic.IEnumerable`1[System.Int32] RunEnumerator[Int32](Int32[])
---Test async calls---
Before: System.Threading.Tasks.Task RunAsync()
Inside: RunAsync()
After: System.Threading.Tasks.Task RunAsync()
Before: System.Threading.Tasks.Task RunAwaitAsync()
Inside: RunAwaitAsync()
After: System.Threading.Tasks.Task RunAwaitAsync()
Before: System.Threading.Tasks.Task`1[System.String] RunAwaitGenericTask[String](System.String)
Inside: RunAwaitGenericTask()
After: System.Threading.Tasks.Task`1[System.String] RunAwaitGenericTask[String](System.String)
Before: System.Collections.Generic.IAsyncEnumerable`1[System.Int32] RunAwaitGenericEnumeratorTask[Int32](Int32[])
Inside Start: RunAwaitGenericEnumeratorTask()
Erg: 1
Erg: 2
Erg: 3
Erg: 4
Inside Ende: RunAwaitGenericEnumeratorTask()
After List: System.Collections.Generic.IAsyncEnumerable`1[System.Int32] RunAwaitGenericEnumeratorTask[Int32](Int32[])
Finally I found the solution. Thankt to Jeroen Mostert for pointing me in the right direction.
namespace ClassLibrary1
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Threading.Tasks;
public static class ObjectExtender
{
internal static bool IsOfGenericType(this object obj, Type check, out Type? genericType)
{
Type actType = obj.GetType();
while (actType != null && actType != typeof(object))
{
if (actType.IsGenericType && actType.GetGenericTypeDefinition() == check.GetGenericTypeDefinition())
{
genericType = actType;
return true;
}
actType = actType.BaseType;
}
genericType = null;
return false;
}
}
public class Class1<T> : DispatchProxy
{
private static readonly MethodInfo AsyncEnumeration;
private static readonly Dictionary<Type, MethodInfo> CachedAsyncEnumerationMethodInfos = new Dictionary<Type, MethodInfo>();
private static readonly Dictionary<Type, MethodInfo> CachedGenericTaskMethodInfos = new Dictionary<Type, MethodInfo>();
private static readonly Dictionary<Type, MethodInfo> CachedSyncEnumerationMethodInfos = new Dictionary<Type, MethodInfo>();
private static readonly MethodInfo GenericTask;
private static readonly MethodInfo SyncEnumeration;
private T _decorated = default!;
static Class1()
{
GenericTask = typeof(Class1<T>).GetMethod("HandleTaskGenericAsync", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
AsyncEnumeration = typeof(Class1<T>).GetMethod("Wrapper", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
SyncEnumeration = typeof(Class1<T>).GetMethod("SyncWrapper", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
}
public static T Create(T decorated)
{
T proxy = Create<T, Class1<T>>();
Class1<T> ap = proxy as Class1<T> ?? throw new ArgumentNullException(nameof(decorated));
ap._decorated = decorated;
return proxy;
}
private static Task<T2> HandleTaskGenericAsync<T1, T2>(T1 result, MethodInfo methodName) where T1 : Task<T2>
{
return result.ContinueWith(parent =>
{
Console.WriteLine($"After: {methodName}");
return parent.Result;
});
}
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
try
{
Console.WriteLine($"Before: {targetMethod}");
object result = targetMethod.Invoke(_decorated, args);
if (result is Task resultTask)
{
if (!resultTask.IsOfGenericType(typeof(Task<>), out Type? genericType))
{
return resultTask.ContinueWith(task =>
{
if (task.Exception != null)
{
Console.WriteLine($"{task.Exception.InnerException ?? task.Exception}, {targetMethod}");
}
else
{
Console.WriteLine($"After: {targetMethod}");
}
});
}
Debug.Assert(genericType != null, nameof(genericType) + " != null");
Type resultType = genericType.GetGenericArguments()[0]; // Task<> hat nur einen.
if (!CachedGenericTaskMethodInfos.ContainsKey(resultType))
{
CachedGenericTaskMethodInfos.Add(resultType, GenericTask.MakeGenericMethod(genericType, resultType));
}
return CachedGenericTaskMethodInfos[resultType].Invoke(null, new object[] {resultTask, targetMethod});
}
Type returnType = targetMethod.ReturnType;
if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(IAsyncEnumerable<>))
{
Type resultType = returnType.GetGenericArguments()[0]; //IAsyncEnumerable hat nur eines
if (!CachedAsyncEnumerationMethodInfos.ContainsKey(resultType))
{
CachedAsyncEnumerationMethodInfos.Add(resultType, AsyncEnumeration.MakeGenericMethod(resultType));
}
return CachedAsyncEnumerationMethodInfos[resultType].Invoke(null, new[] {result, targetMethod});
}
if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
{
Type resultType = returnType.GetGenericArguments()[0]; //IAsyncEnumerable hat nur eines
if (!CachedSyncEnumerationMethodInfos.ContainsKey(resultType))
{
CachedSyncEnumerationMethodInfos.Add(resultType, SyncEnumeration.MakeGenericMethod(resultType));
}
return CachedSyncEnumerationMethodInfos[resultType].Invoke(null, new[] {result, targetMethod});
}
Console.WriteLine($"After: {targetMethod}");
return result;
}
catch (TargetInvocationException ex)
{
Console.WriteLine($"{ex.InnerException ?? ex}, {targetMethod}");
throw;
}
}
private static IEnumerable<T> SyncWrapper<T>(IEnumerable<T> inner, MethodInfo targetMethod)
{
foreach (T t in inner)
{
yield return t;
}
Console.WriteLine($"After List: {targetMethod}");
}
private static async IAsyncEnumerable<T> Wrapper<T>(IAsyncEnumerable<T> inner, MethodInfo targetMethod)
{
await foreach (T t in inner)
{
yield return t;
}
Console.WriteLine($"After List: {targetMethod}");
}
}
}

How to call Action<string, bool> from il generator

In this example code i am trying to invoke a anonymous action from il generator. I am not sure if and how i can load the reference to the delegate and how to call it.
I can do it if the OnFunctionCall is a static method not property.
public delegate void TestDelegate();
public static class ExampleOne
{
public static Action<string, bool> OnFunctionCall
=> (message, flag) => Console.WriteLine("Example");
}
public static class ExampleTwo
{
public static TType CreateDelegate<TType>(Action<string, bool> onFunctionCall)
where TType : class
{
var method = new DynamicMethod($"{Guid.NewGuid()}", typeof(void), Type.EmptyTypes, typeof(TType), true);
ILGenerator il = method.GetILGenerator();
// Emit some code that invoke unmanaged function ...
// loading the first string argument
il.Emit(OpCodes.Ldstr, method.Name);
// not sure here how to load boolean value to the stack
il.Emit(OpCodes.Ldc_I4_0);
// this line doesn't work
// example two has no idea about ExampleOne
// is it possible to load the reference of the Action<string, bool> to the stack and call it ?
il.Emit(OpCodes.Call, onFunctionCall.Method);
il.Emit(OpCodes.Ret);
return method.CreateDelegate(typeof(TestDelegate)) as TType;
}
}
public class Program
{
public static void Main(string[] args)
=> ExampleTwo
.CreateDelegate<TestDelegate>(ExampleOne.OnFunctionCall)
.Invoke();
}
You have to pass the information where the delegate you want to invoke is stored. The convenience way is to accept a MemberExpression, otherwise accepting a MemberInfowould work too. Have a look at your modified code:
public delegate void TestDelegate();
public static class ExampleOne
{
public static Action<string, bool> OnFunctionCall
=> (message, flag) => Console.WriteLine("OnFunctionCall");
public static Action<string, bool> OnFunctionCallField
= (message, flag) => Console.WriteLine("OnFunctionCallField");
}
public static class ExampleTwo
{
public static TType CreateDelegate<TType>(Expression<Func<object>> expression)
where TType : class
{
var body = expression.Body as MemberExpression;
if (body == null)
{
throw new ArgumentException(nameof(expression));
}
var method = new DynamicMethod($"{Guid.NewGuid()}", typeof(void), Type.EmptyTypes, typeof(TType), true);
ILGenerator il = method.GetILGenerator();
// Get typed invoke method and
// call getter or load field
MethodInfo invoke;
if (body.Member is PropertyInfo pi)
{
invoke = pi.PropertyType.GetMethod("Invoke");
il.Emit(OpCodes.Call, pi.GetGetMethod());
}
else if (body.Member is FieldInfo fi)
{
invoke = fi.FieldType.GetMethod("Invoke");
il.Emit(OpCodes.Ldsfld, fi);
}
else
{
throw new ArgumentException(nameof(expression));
}
il.Emit(OpCodes.Ldstr, method.Name);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Callvirt, invoke);
il.Emit(OpCodes.Ret);
return method.CreateDelegate(typeof(TestDelegate)) as TType;
}
}
public class Program
{
public static void Main(string[] args)
{
ExampleTwo
.CreateDelegate<TestDelegate>(() => ExampleOne.OnFunctionCall)
.Invoke();
ExampleTwo
.CreateDelegate<TestDelegate>(() => ExampleOne.OnFunctionCallField)
.Invoke();
Console.ReadLine();
}
}
The code is running on .Net Core 2.0.

Invoke methodinfo inherited from closed generic

private static void Main(string[] args)
{
var messageType = typeof (SampleHandler1);
var genericType = typeof (IConsume<>).MakeGenericType(messageType);
var genericArguments = genericType.GetGenericArguments();
var consumeMethod = genericType.GetMethod("Consume");
var constructorInfo = genericArguments[0].GetConstructor(Type.EmptyTypes);
var classObject = constructorInfo.Invoke(new object[] {});
var argsx = new object[] {new SampleMessage {Name = "sample message"}};
consumeMethod.Invoke(classObject, argsx);
}
public interface IConsume<in T> where T : class
{
void Consume(T message);
}
public class SampleHandler1 : IConsume<SampleMessage>
{
public SampleHandler1()
{
Debugger.Break();
}
public void Consume(SampleMessage message)
{
Debugger.Break();
Console.WriteLine("Message consume: " + message.Name);
}
}
public interface IBaseMessage
{
}
public class SampleMessage : IBaseMessage
{
public string Name { get; set; }
}
I tried looking here but I cant find specific solution. As MSDN explains
obj
Type: System.Object
The object on which to invoke the method or constructor. If a method is static, this argument is ignored. If a constructor is static, this argument must be null or an instance of the class that defines the constructor.
classObject is an instance of constructor, right? Why it throws an exception:
That doesn't seem right. Let's analyze what's happening here:
var messageType = typeof (SampleHandler1);
//simple enough, Type -> SampleHandler1
var genericType = typeof (IConsume<>).MakeGenericType(messageType);
//so genericType is a Type -> IConsume<SampleHandler1>
var genericArguments = genericType.GetGenericArguments();
//there's only one, but Type[] { Type -> SampleHandler1 }
var consumeMethod = genericType.GetMethod("Consume");
//MethodInfo -> IConsume<SampleHandler1>.Consume(SampleHandler1)
var constructorInfo = genericArguments[0].GetConstructor(Type.EmptyTypes);
//ConstructorInfo -> SampleHandler1..ctor()
var classObject = constructorInfo.Invoke(new object[] {});
//new SampleHandler1()
var argsx = new object[] {new SampleMessage {Name = "sample message"}};
//object[] { SampleMessage }
consumeMethod.Invoke(classObject, argsx);
//((IConsume<SampleHandler1>)classObject).Consume( SampleMessage ) -- oops?
So classObject is an SampleHandler1, but you're trying to invoke IConsume<SampleHandler1>.Consume(SampleHandler1) and worse yet give it a SampleMessage as an argument.
I think you meant to create a SampleHandler1, and invoke IConsume<SampleMessage>.Consume(SampleMessage) on it:
var messageType = typeof(SampleMessage);
var genericType = typeof(IConsume<>).MakeGenericType(messageType);
var consumeMethod = genericType.GetMethod("Consume");
var handlerType = typeof(SampleHandler1);
var constructorInfo = handlerType.GetConstructor(Type.EmptyTypes);
var classObject = constructorInfo.Invoke(new object[] {});
var argsx = new object[] {new SampleMessage {Name = "sample message"}};
consumeMethod.Invoke(classObject, argsx);
I'm not sure, but based on all of the components you have in your question, I suspect that you are looking for something more like this:
using System;
public class Program
{
public static void Main()
{
var handlerType = typeof (SampleHandler1);
var genericType = handlerType.GetInterface("IConsume`1");
var genericArguments = genericType.GetGenericArguments();
var consumeMethod = genericType.GetMethod("Consume");
var handlerConstructorInfo = handlerType.GetConstructor(Type.EmptyTypes);
var handler = handlerConstructorInfo.Invoke(new object[] {});
var messageConstructorInfo = genericArguments[0].GetConstructor(Type.EmptyTypes);
var messageObject = messageConstructorInfo.Invoke(new object[] {});
((IBaseMessage)messageObject).Name = "Sample Message";
var argsx = new object[] {messageObject};
consumeMethod.Invoke(handler, argsx);
}
}
public interface IConsume<in T> where T : class, IBaseMessage
{
void Consume(T message);
}
public class SampleHandler1 : IConsume<SampleMessage>
{
public SampleHandler1()
{
Console.WriteLine("SampleHandler1 constructed");
}
public void Consume(SampleMessage message)
{
Console.WriteLine("Message consume: " + message.Name);
}
}
public interface IBaseMessage
{
string Name { get; set; }
}
public class SampleMessage : IBaseMessage
{
public string Name { get; set; }
}
Here is a working Dotnetfiddle of the above answer: https://dotnetfiddle.net/YFmmzk
The console output is:
SampleHandler1 constructed
Message consume: Sample Message
It looks like you were getting your handler and message types confused. You were trying to pass an instance of the handler itself to the consume method. More over, IBaseMessage was missing the Name property declaration.
UPDATE
Here is a cleaned up version of this answer:
public class Program
{
public static void Main()
{
var handler = new DynamicConstructor(typeof (SampleHandler1)).New();
invokeIConsumeFor(handler, "Sample Message");
}
private static void invokeIConsumeFor(object handler, string message)
{
var executer = new DynamicGenericInterfaceExecuter(handler, "IConsume`1");
var messageObject = executer.GetTypeArgumentConstructor(0, Type.EmptyTypes).New();
((IBaseMessage) messageObject).Name = message;
executer.Method("Consume", messageObject.GetType()).Call(messageObject);
}
}
public class DynamicGenericInterfaceExecuter
{
private object instance;
private Type genericInterfaceFromType;
private Type[] genericTypeArguments;
public DynamicGenericInterfaceExecuter(object instance, string interfaceName)
{
this.instance = instance;
this.genericInterfaceFromType = instance.GetType().GetInterface(interfaceName);
this.genericTypeArguments = this.genericInterfaceFromType.GetGenericArguments();
}
public MethodExecuter Method(string methodName, params Type[] parameterTypes)
{
return new MethodExecuter(this.instance, this.genericInterfaceFromType, methodName, parameterTypes);
}
public DynamicConstructor GetTypeArgumentConstructor(int typeArgumentIndex, params Type[] constructorParameterTypes)
{
return new DynamicConstructor(this.genericTypeArguments[typeArgumentIndex], constructorParameterTypes);
}
}
public class DynamicConstructor
{
private System.Reflection.ConstructorInfo constructor;
public DynamicConstructor(Type type, params Type[] constructorParameters)
{
this.constructor = type.GetConstructor(constructorParameters);
}
public object New(params object[] constructorArguments)
{
return this.constructor.Invoke(constructorArguments);
}
}
public class MethodExecuter
{
private object instance;
private System.Reflection.MethodInfo method;
public MethodExecuter(object instance, Type containerType, string methodName, Type[] methodParameters)
{
this.instance = instance;
this.method = containerType.GetMethod(methodName, methodParameters);
}
public void Call(params object[] arguments)
{
this.Invoke(arguments);
}
public object Invoke(params object[] arguments)
{
return this.method.Invoke(instance, arguments);
}
}
public interface IConsume<in T> where T : class, IBaseMessage
{
void Consume(T message);
}
public class SampleHandler1 : IConsume<SampleMessage>
{
public SampleHandler1()
{
Console.WriteLine("SampleHandler1 constructed");
}
public void Consume(SampleMessage message)
{
Console.WriteLine("Message consume: " + message.Name);
}
}
public interface IBaseMessage
{
string Name { get; set; }
}
public class SampleMessage : IBaseMessage
{
public string Name { get; set; }
}
And the dotnetfiddle: https://dotnetfiddle.net/n9WHZ2
Keep in mind that this is not type safe in the slightest, but that does not appear to be a concern in your question.

Code Emit and Expressions

I am using Code Emit for dynamic code generation.
I would like to set a field using an external factory method.
Here is my (reduced) code:
Defines:
Func<object> fact = () => new B();
var mi = fact.GetMethodInfo();
var t = typeof(B);
Emit code:
ILGenerator ilg;
var tb = _mb.DefineType("myProxy", TypeAttributes.Public | TypeAttributes.Class, typeof(object));
var fieldBuilder = tb.DefineField("proxy", t, FieldAttributes.Private);
var ctorBuilder = tb.DefineConstructor(...);
ilg = ctorBuilder.GetILGenerator();
ilg.Emit(OpCodes.Ldarg_0);
ilg.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));
ilg.Emit(OpCodes.Ldarg_0);
ilg.Emit(OpCodes.Callvirt, mi);
ilg.Emit(OpCodes.Castclass, t);
ilg.Emit(OpCodes.Stfld, fieldBuilder);
ilg.Emit(OpCodes.Ret);
Create an instace:
Activator.CreateInstance(tb.CreateType());
TargetInvocationException is thrown
{"Method not found: \"?\"."}
Here is what I am looking forward to generate:
public class A
{
private B _proxy;
public A(Func<object> factory)
{
_proxy = (B)factory();
}
}
BUT the factory Method is fixed and not provided as parameter...
public class A
{
private B _proxy;
public A()
{
_proxy = (B) //[GENERATE ME] () => new B();
}
}
Any Suggestions?
You have to invoke delegate via "Invoke" method but you need to give factory delegate to your proxy. You can add a parameter to your proxy constructor.
public class B
{
}
static internal class Metadata<T> //Avoid lock & string metadata description
{
static public readonly Type Type = typeof(T);
static public FieldInfo Field<X>(Expression<Func<T, X>> expression)
{
return (expression.Body as MemberExpression).Member as FieldInfo;
}
static public PropertyInfo Property<X>(Expression<Func<T, X>> expression)
{
return (expression.Body as MemberExpression).Member as PropertyInfo;
}
static public MethodInfo Method(Expression<Action<T>> expression)
{
return (expression.Body as MethodCallExpression).Method;
}
static public MethodInfo Method<X>(Expression<Func<T, X>> expression)
{
return (expression.Body as MethodCallExpression).Method;
}
}
class Program
{
static void Main(string[] args)
{
var _Factory = new Func<object>(() => new B());
TypeBuilder _TypeBuilder = null;// = ...;
var _Parameters = new Type[] { Metadata<Func<object>>.Type };
var _Constructor = _TypeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, _Parameters);
var _Body = _Constructor.GetILGenerator();
//...
_Body.Emit(OpCodes.Ldarg_1);
_Body.Emit(OpCodes.Call, Metadata<Func<object>>.Method(_Func => _Func.Invoke()));
//...
var _Type = _TypeBuilder.CreateType();
var _Parameter = Expression.Parameter(Metadata<Func<object>>.Type);
var _New = Expression.Lambda<Func<Func<object>, object>>(Expression.New(_Type.GetConstructor(_Parameters), _Parameter), _Parameter).Compile();
var _Instance = _New(_Factory);
}
}

Categories

Resources