Olá pessoal, como estamos?
Este é o terceiro e último post de uma (mini) série mostrando como criar proxies dinâmicos com emitting.
No primeiro post, mostrei como criar objetos dinamicamente que repassam chamadas de métodos para implementações concretas (proxies).
No segundo post, mostrei como introduzir um “monitor” que seria “avisado” toda vez que um método fosse evocando, antes (BeforeExecute) e depois (AfterExecute).
Hoje, finalizo a série com um pouco de refactoring e a criação de monitores com suporte a Expressions..
Todo código está disponível em https://github.com/elemarjr/FluentIL
Etapa …17 – Refactoring
Encerrei o post anterior mencionando que o código tinha um Bad smell sério, meus métodos estavam ficando grandes demais. Minhas diretrizes foram:
- tentar manter métodos com menos de 10 linhas;
- remover condicionais explícitos (retirar os if do código);
- utilizar fluência para deixar o código mais legível.
O resultado final ficou bacana. Observe:
public static class ProxyBuilder
{
public static T CreateProxy<T>(
T concreteInstance,
IProxyMonitor monitor = null
)
{
return CreateInstance<T>(
IL.NewType().Implements<T>()
.EmitConcreteInstanceSupport<T>()
.EmitProxyMonitorSupport(condition: monitor != null)
.EmitMethods<T>(monitor),
concreteInstance, monitor);
}
private static T CreateInstance<T>(
this DynamicTypeInfo that,
T concreteInstance,
IProxyMonitor monitor = null
)
{
var type = that.AsType;
var result = (T)Activator.CreateInstance(type);
var setupConcreteInstance = type.GetMethod("__SetConcreteInstance");
setupConcreteInstance.Invoke(result, new object[] { concreteInstance });
if (monitor != null)
{
var setupProxyMonitor = type.GetMethod("__SetProxyMonitor");
setupProxyMonitor.Invoke(result, new object[] { monitor });
}
return result;
}
private static DynamicTypeInfo EmitMethods<T>(
this DynamicTypeInfo that,
IProxyMonitor monitor
)
{
foreach (var method in typeof(T).GetMethods())
EmitMethod(that, method, monitor);
return that;
}
private static void EmitMethod(
DynamicTypeInfo t,
MethodInfo method,
IProxyMonitor monitor = null
)
{
var body = EmitMethodSignature(t, method, monitor)
.EmitBeforeExecuteCall(condition: monitor != null,
method: method)
.EmitConcreteMethodCall(method)
.EmitAfterExecuteCall(condition: monitor != null,
method: method)
.Ret();
}
private static DynamicMethodBody EmitConcreteMethodCall(
this DynamicMethodBody body,
MethodInfo method )
{
var parameters = method.GetParameters();
return body
.Ldarg(0)
.Ldfld("__concreteinstance")
.Repeater(0, parameters.Length - 1, 1, (index, b) => b
.Ldarg(parameters[index].Name)
)
.Call(method);
}
private static DynamicMethodBody EmitMethodSignature(
DynamicTypeInfo t,
MethodInfo method,
IProxyMonitor monitor)
{
var ilmethod = t.WithMethod(method.Name);
var parameters = method.GetParameters();
foreach (var param in parameters)
ilmethod.WithParameter(
param.ParameterType,
param.Name
);
if (monitor != null)
{
if (method.ReturnType != typeof(void))
ilmethod.WithVariable(method.ReturnType);
if (parameters.Length > 0)
ilmethod.WithVariable(typeof(object[]), "parameters");
}
return ilmethod
.Returns(method.ReturnType);
}
private static DynamicMethodBody EmitBeforeExecuteCall(
this DynamicMethodBody body,
bool condition,
MethodInfo method
)
{
if (!condition) return body;
var beforeExecuteMi = typeof(IProxyMonitor)
.GetMethod("BeforeExecute");
var parameters = method.GetParameters();
return body
.Ldarg(0).Dup()
.Ldfld("__proxymonitor")
.Ldstr(method.Name)
.Newarr(typeof(object), parameters.Length)
.EmitIf( parameters.Length > 0, b => b
.Stloc("parameters")
.Repeater(0, parameters.Length - 1, 1, (index, b1) => b1
.Ldloc("parameters")
.Ldc(index)
.Ldarg((uint)(index + 1))
.Box(parameters[index].ParameterType)
.Emit(OpCodes.Stelem_Ref)
)
.Ldloc("parameters")
)
.Call(beforeExecuteMi)
//
.Ldfld("__proxymonitor")
.Ldstr(method.Name);
}
private static DynamicMethodBody EmitAfterExecuteCall(
this DynamicMethodBody body,
bool condition,
MethodInfo method )
{
if (!condition) return body;
var afterExecuteMi = typeof(IProxyMonitor)
.GetMethod("AfterExecute");
if (method.ReturnType != typeof(void))
body
.Stloc(0)
.Ldloc(0)
.Box(method.ReturnType);
else
body.Ldnull();
body
.Call(afterExecuteMi);
if (method.ReturnType != typeof(void))
body.Ldloc(0);
return body;
}
private static DynamicTypeInfo EmitConcreteInstanceSupport<T>(
this DynamicTypeInfo that)
{
that
.WithField("__concreteinstance", typeof(T))
.WithMethod("__SetConcreteInstance")
.WithParameter(typeof(T))
.Returns(typeof(void))
.Ldarg(0)
.Ldarg(1)
.Stfld("__concreteinstance")
.Ret();
return that;
}
private static DynamicTypeInfo EmitProxyMonitorSupport(
this DynamicTypeInfo that,
bool condition
)
{
if (!condition) return that;
that
.WithField("__proxymonitor", typeof(IProxyMonitor))
.WithMethod("__SetProxyMonitor")
.WithParameter(typeof(IProxyMonitor))
.Returns(typeof(void))
.Ldarg(0)
.Ldarg(1)
.Stfld("__proxymonitor")
.Ret();
return that;
}
}
Etapa 18 – Criar um monitor genérico com suporte a Expressions
Ter que implementar uma classe inteira toda vez que desejamos “monitorar” o consumo de uma classe parece ser um desperdício. Por causa disso, criei uma implementação genérica. Observe:
public class ExpressionProxyMonitor
: IProxyMonitor
{
public Action<string, object []> BeforeExecuteAction { get; set; }
public void BeforeExecute(string methodName, object[] p)
{
if (this.BeforeExecuteAction == null)
return;
this.BeforeExecuteAction(methodName, p);
}
public Action<string, object> AfterExecuteAction { get; set; }
public void AfterExecute(string methodName, object result)
{
if (this.AfterExecuteAction == null)
return;
this.AfterExecuteAction(methodName, result);
}
}
Agora, posso utilizar essa classe para criar “monitores” mais rapidamente.
Etapa 19 – Que tal um helper?!
Resolvi adicionar um helper para tornar a criação do proxy mais expressiva. Eis o método que incluí em ProxyBuilder.
public static T CreateProxy<T>(
T concreteInstance,
Action<string, object []> beforeExecuteAction,
Action<string, object> afterExecuteAction
)
{
var monitor = new ExpressionProxyMonitor()
{
BeforeExecuteAction = beforeExecuteAction,
AfterExecuteAction = afterExecuteAction
};
return CreateProxy<T>(concreteInstance, monitor);
}
Pronto! Estamos mais expressivos.
Etapa 20 – Testando nosso “monitor genérico”
Vamos ao nosso teste. Afinal, temos que garantir que nosso monitor funciona
… Ou, pelo menos, não possui erros evidentes.
[Test]
public void CreateProxy_PassingExpressions()
{
// arrange
var foo = new Foo();
int cc = 0;
var target = ProxyBuilder.CreateProxy<IFoo>(
foo,
beforeExecuteAction: (s, o) =>
cc += 2,
afterExecuteAction: (s, o) =>
cc--
);
// act
target.Add(2, 3);
// assert
cc.Should().Be(1);
}
Green bar!!
Enfim, o fim! ![]()
Por hoje, era isso.






março 28th, 2012 → 23:30
[...] Depois, também mostrei como criar proxies dinamicamente (em três posts – 1/3, 2/3 e 3/3). [...]