|
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
using Pfz.Collections;
using Pfz.DynamicObjects.Internal;
using Pfz.Extensions;
using Pfz.Threading;
namespace Pfz.DynamicObjects
{
/// <summary>
/// Class used to implement any delegate and call a proxy object to process the invokes.
/// </summary>
public static class DelegateProxier
{
#region Pair - Nested Class
private sealed class Pair
{
public ConstructorInfo Constructor;
public MethodInfo Method;
}
#endregion
#region Fields
private static readonly Dictionary<ImmutableArray<Type>, Pair> _implementations = new Dictionary<ImmutableArray<Type>, Pair>();
private static readonly ReaderWriterLockSlim _implementationsLock = new ReaderWriterLockSlim();
private static readonly MethodInfo _invokeMethod = typeof(IProxyDelegate).GetMethod("Invoke");
private static readonly Type[] _proxyTypes = new Type[]{typeof(IProxyDelegate)};
#endregion
#region Proxy
/// <summary>
/// Implementes a delegate and calls the given proxy.
/// </summary>
public static Delegate Proxy(IProxyDelegate delegateProxy, Type delegateType)
{
if (delegateProxy == null)
throw new ArgumentNullException("delegateProxy");
if (delegateType == null)
throw new ArgumentNullException("delegateType");
if (!delegateType.IsSubclassOf(typeof(Delegate)))
throw new ArgumentException("delegateType must be a delegate.", "delegateType");
var sourceInvoke = delegateType.GetMethod("Invoke");
var returnType = sourceInvoke.ReturnType;
List<Type> parameterTypes = new List<Type>();
parameterTypes.Add(returnType);
var parameters = sourceInvoke.GetParameters();
int parameterCount = parameters.Length;
foreach(var parameter in parameters)
parameterTypes.Add(parameter.ParameterType);
var immutableArray = new ImmutableArray<Type>(parameterTypes);
Pair pair;
using(_implementationsLock.ReadLock())
_implementations.TryGetValue(immutableArray, out pair);
if (pair == null)
{
using(_implementationsLock.UpgradeableLock())
{
if (!_implementations.TryGetValue(immutableArray, out pair))
{
var type =
_DynamicModule.DefineType
(
"Pfz.DynamicObjects.DelegateImplementations." + delegateType.Name,
TypeAttributes.Public | TypeAttributes.Sealed,
typeof(BaseImplementedProxy),
Type.EmptyTypes
);
var constructor =
type.DefineConstructor
(
MethodAttributes.Public,
CallingConventions.Standard,
_proxyTypes
);
var constructorGenerator = constructor.GetILGenerator();
constructorGenerator.Emit(OpCodes.Ldarg_0);
constructorGenerator.Emit(OpCodes.Ldarg_1);
constructorGenerator.Emit(OpCodes.Call, InterfaceProxier._baseImplementedProxyConstructorInfo);
constructorGenerator.Emit(OpCodes.Ldarg_0);
constructorGenerator.Emit(OpCodes.Ldarg_1);
constructorGenerator.Emit(OpCodes.Stfld, InterfaceProxier._baseImplementedProxyFieldInfo);
constructorGenerator.Emit(OpCodes.Ret);
var method =
type.DefineMethod
(
"Invoke",
MethodAttributes.Public,
returnType,
sourceInvoke.GetParameterTypes()
);
var methodGenerator = method.GetILGenerator();
methodGenerator.Emit(OpCodes.Ldarg_0);
methodGenerator.Emit(OpCodes.Ldfld, InterfaceProxier._baseImplementedProxyFieldInfo);
if (parameters.Length == 0)
{
methodGenerator.Emit(OpCodes.Ldnull);
}
else
{
methodGenerator.DeclareLocal(typeof(object[]));
methodGenerator.Emit(OpCodes.Ldc_I4, parameterCount);
methodGenerator.Emit(OpCodes.Newarr, typeof(object));
methodGenerator.Emit(OpCodes.Stloc_0);
int parameterIndex = -1;
foreach(var parameter in parameters)
{
parameterIndex++;
methodGenerator.Emit(OpCodes.Ldloc_0);
methodGenerator.Emit(OpCodes.Ldc_I4, parameterIndex);
methodGenerator.Emit(OpCodes.Ldarg, parameterIndex+1);
var parameterType = parameter.ParameterType;
var effectiveType = parameterType;
if (parameterType.IsByRef)
{
effectiveType = parameterType.GetElementType();
methodGenerator.Emit(OpCodes.Ldobj, effectiveType);
}
if (parameterType.IsValueType)
methodGenerator.Emit(OpCodes.Box, effectiveType);
methodGenerator.Emit(OpCodes.Stelem_Ref);
}
methodGenerator.Emit(OpCodes.Ldloc_0);
}
methodGenerator.Emit(OpCodes.Callvirt, _invokeMethod);
for (int i = 0; i < parameterCount; i++)
{
var parameter = parameters[i];
var parameterType = parameter.ParameterType;
if (!parameterType.IsByRef)
continue;
parameterType = parameterType.GetElementType();
methodGenerator.Emit(OpCodes.Ldarg, i + 1);
methodGenerator.Emit(OpCodes.Ldloc_0);
methodGenerator.Emit(OpCodes.Ldc_I4, i);
methodGenerator.Emit(OpCodes.Ldelem_Ref);
if (parameterType.IsValueType)
methodGenerator.Emit(OpCodes.Unbox_Any, parameterType);
methodGenerator.Emit(OpCodes.Stobj, parameterType);
}
if (returnType == typeof(void))
methodGenerator.Emit(OpCodes.Pop);
else
if (returnType.IsValueType)
methodGenerator.Emit(OpCodes.Unbox_Any, returnType);
methodGenerator.Emit(OpCodes.Ret);
var realType = type.CreateType();
pair = new Pair();
pair.Constructor = realType.GetConstructor(_proxyTypes);
pair.Method = realType.GetMethod("Invoke");
using(_implementationsLock.WriteLock())
_implementations.Add(immutableArray, pair);
}
}
}
var result = pair.Constructor.Invoke(new object[]{delegateProxy});
return Delegate.CreateDelegate(delegateType, result, pair.Method);
}
#endregion
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
I started to program computers when I was 11 years old, as a hobbyist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.
At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do their work easier, faster and with less errors.
Want more info or simply want to contact me?
Take a look at:
http://paulozemek.azurewebsites.net/
Or e-mail me at: paulozemek@outlook.com
Codeproject MVP 2012, 2015 & 2016
Microsoft MVP 2013-2014 (in October 2014 I started working at Microsoft, so I can't be a Microsoft MVP anymore).