动态装饰器、Unity和Castle DynamicProxy比较






4.50/5 (3投票s)
比较动态装饰器、Unity和Castle DynamicProxy在扩展对象功能方面的性能和特性
引言
通过附加行为来扩展对象的功能比修改现有类或创建新类来扩展功能更有优势。但是,它的采用仍然有限。我认为原因是:要么可用的工具过于复杂,要么缺乏进行基于它们的严肃编程的功能。这种情况现在正在改变。随着新工具的出现,它们具有更多功能,并且仍然易于使用,开发人员应该认真考虑通过附加行为来扩展对象的功能,而不是通过修改现有类或创建新类来扩展功能。
.NET世界中有三种工具可用于扩展对象的功能。它们是Castle DynamicProxy、Unity和动态装饰器。本文讨论了它们在扩展对象功能方面的性能和特性。
范围
动态装饰器中`ObjectProxyFactory`类的`CreateProxy`方法与Castle DynamicProxy中`ProxyGenerator`类的`CreateInterfaceProxyWithTarget`方法和Unity中`Intercept`的`ThroughProxy`方法非常接近。因此,讨论仅限于使用它们向实现接口的现有对象添加一些预处理/后处理功能。
测试代码
测试代码如下所示。
//EnterLogBehavior class for Unity
public class EnterLogBehavior : IInterceptionBehavior
{
public IEnumerable<Type> GetRequiredInterfaces()
{
return Type.EmptyTypes;
}
public bool WillExecute
{
get { return true; }
}
public IMethodReturn Invoke(IMethodInvocation input,
GetNextInterceptionBehaviorDelegate getNext)
{
Console.Write("Calling: " + input.MethodBase.Name + "\n");
var methodReturn = getNext().Invoke(input, getNext);
return methodReturn;
}
}
//ExitLogBehavior class for Unity
public class ExitLogBehavior : IInterceptionBehavior
{
public IEnumerable<Type> GetRequiredInterfaces()
{
return Type.EmptyTypes;
}
public bool WillExecute
{
get { return true; }
}
public IMethodReturn Invoke(IMethodInvocation input,
GetNextInterceptionBehaviorDelegate getNext)
{
var methodReturn = getNext().Invoke(input, getNext);
Console.Write("Exiting: " + input.MethodBase.Name + "\n");
return methodReturn;
}
}
//LoggingInterceptor class for Castle DynamicProxy
public class LoggingInterceptor : Castle.DynamicProxy.IInterceptor
{
private int m_count;
public void Intercept(IInvocation invocation)
{
Console.Write("Calling: " + invocation.Method.Name + "\n");
invocation.Proceed();
Console.Write("Exiting: " + invocation.Method.Name + "\n");
}
}
class Program
{
static void Main(string[] args)
{
IEmployee emp = new Employee(1, "John", "Smith", new DateTime(1990, 4, 1), 1);
System.Int32? id = null;
System.String detail = "";
IEmployee iemp;
TimeSpan? ts = null;
System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();
//Performance test for Unity
stopWatch.Start();
iemp = Intercept.ThroughProxy<IEmployee>(emp, new InterfaceInterceptor(),
new[] { (IInterceptionBehavior)new EnterLogBehavior(),
(IInterceptionBehavior)new ExitLogBehavior() });
id = iemp.EmployeeID;
detail = iemp.DetailsByLevel(2);
stopWatch.Stop();
ts = stopWatch.Elapsed;
Console.Write("Using Unity ThroughProxy: " + ts.Value.ToString() + "\n\n");
//Performance test for Castle DynamicProxy
stopWatch.Restart();
ProxyGenerator generator = new ProxyGenerator();
iemp = generator.CreateInterfaceProxyWithTarget<IEmployee>
(emp, new LoggingInterceptor());
id = iemp.EmployeeID;
detail = iemp.DetailsByLevel(2);
stopWatch.Stop();
ts = stopWatch.Elapsed;
Console.Write("Using Castle CreateInterfaceProxyWithTarget: "
+ ts.Value.ToString() + "\n\n");
//Performance test for Dynamic Decorator
stopWatch.Restart();
iemp = ObjectProxyFactory.CreateProxy<IEmployee>(
emp,
new String[] { "DetailsByLevel", "get_EmployeeID" },
new Decoration((x, y) =>
{
Console.Write("Calling: " + x.CallCtx.MethodName + "\n");
}, null),
new Decoration((x, y) =>
{
Console.Write("Exiting: " + x.CallCtx.MethodName + "\n");
}, null));
id = iemp.EmployeeID;
detail = iemp.DetailsByLevel(2);
stopWatch.Stop();
ts = stopWatch.Elapsed;
Console.Write("Using Dynamic Decorator: " + ts.Value.ToString() + "\n\n");
//More features for Dynamic Decorator
emp = ObjectProxyFactory.CreateProxy<IEmployee>(
emp,
new String[] { "DetailsByLevel" },
new Decoration((x, y) =>
{
IMethodCallMessage method = x.CallCtx;
string str = "Calling " + x.Target.GetType().ToString() + "."
+ method.MethodName +
"(";
int i = 0;
foreach (object o in method.Args)
{
if (i > 0)
str = str + ", ";
str = str + o.ToString();
}
str = str + ")";
Console.WriteLine(str);
Console.Out.Flush();
}, null),
null);
id = emp.DepartmentID;
detail = emp.DetailsByLevel(2);
}
}
在上面的代码中,创建了类`Employee`的对象`emp`。首先,使用Unity的`Intercept`类的`ThroughProxy`方法向对象`emp`添加进入/退出日志。其次,使用Castle DynamicProxy的`ProxyGenerator`的`CreateInterfaceProxyWithTarget`方法向对象`emp`添加进入/退出日志。第三,使用动态装饰器的`ObjectProxyFactory`类的`CreateProxy`方法向对象`emp`添加进入/退出日志。
执行上述代码期间,您将看到以下输出

首先,让我们看一下每个工具的性能。对于完全相同的操作,Unity花费了111.3毫秒,Castle DynamicProxy花费了248.3毫秒,而动态装饰器花费了3.9毫秒。尽管它们都声明自己是轻量级工具,但您可以看到哪个工具在向对象添加额外行为时真正轻量级。在性能方面,动态装饰器显然是赢家。
其次,让我们看看每个工具的行为是如何定义的。对于Unity,每个行为都是一个实现`IInterceptionBehavior`接口的类,例如`EnterLogBehavior`和`ExitLogBehavior`。对于Castle DynamicProxy,行为在一个名为`LoggingInterceptor`的类中,该类实现了`Castle.DynamicProxy.IInterceptor`接口。对于动态装饰器,每个行为都是一个方法(此示例的lambda表达式)。因此,从概念上讲,动态装饰器更简单。
接下来,让我们看看动态装饰器中可用的更多功能。对于Unity和Castle DynamicProxy,行为附加到对象的所有方法。没有简单的方法来指定对象哪些方法应该具有行为。要么所有方法都有行为,要么没有方法有行为。但是,使用动态装饰器,可以指定单个方法具有额外行为。例如,在代码的最后一部分,在调用动态装饰器的`ObjectProxyFactory.CreateProxy`时,只指定了`DetailsByLevel`方法。结果,只有`DetailsByLevel`方法获得了额外行为。对象的其它方法没有获得额外行为。因此,动态装饰器更灵活、更细粒度。
最后,动态装饰器中定义的行为能够访问目标对象和调用上下文。获取运行时信息非常方便。例如,上面代码中最后一个`ObjectProxyFactory.CreateProxy`的lambda表达式访问目标对象以获取类型,并访问调用上下文以获取方法名称和参数值。另一方面,我不知道有任何处理Unity或Castle DynamicProxy的上下文或目标信息的示例。
动态装饰器还有其他功能可用于增强行为。有关更多详细信息和示例,请参阅文章使用动态装饰器向对象添加方面。
结论
动态装饰器专门用于扩展对象的功能。它易于使用、灵活,可用于编写极其强大的行为。它是一个真正轻量级且功能丰富的工具,用于扩展对象功能和向对象添加方面。另一方面,虽然Unity和Castle DynamicProxy可用于扩展对象的功能,但它们具有明显的性能开销和有限的功能来增强行为。
历史
- 2011年7月26日:初始发布