怎样Interlocked.Increment一个反射得到的field?
原始的问题是:“如何修改一个用reflection得到的field值,并且保证线程安全”。根据所采用的同步技术,解决方案各有不同。如果是采用lock,那么自然可以反射得到同步对象然后使用Monitor来同步。但如果对象采用的是Interlocked那一套方法,问题就变得有些麻烦了。其中最典型的,也就是如何Increment一个reflection得到的int型field。困难之处在于Increment方法需要ref int参数(编译时),而若使用FieldInfo的GetValue方法只能得到box后的值(运行时),对其进行修改已经没有意义。既然前者的需求无法在编译时满足,那就干脆把它的编译推迟。使用Expression Tree或者类似技术可以做到。下面展示如何自增目标类Foo中具有CounterAttribute的字段。
// This attribute indicates the field to be increased. [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] public class CounterAttribute : Attribute { } public class Foo { // This counter will be increased. [Counter] public long Counter = 0; } class Program { static void Main(string[] args) { var foo = new Foo(); // Print the counter before increasement. Console.WriteLine(foo.Counter); // Do the increasement. IncreaseCounter(foo); // Print the increased counter. Console.WriteLine(foo.Counter); } private static void IncreaseCounter(object instance) { // Get the target type whose public field is to be increased. var targetType = instance.GetType(); // Get the target field. var targetField = targetType.GetFields(BindingFlags.Public | BindingFlags.Instance) .First(p => p.GetCustomAttributes(typeof (CounterAttribute), false).Any()); // The Interlocked.Increment method var increaseMethod = typeof (Interlocked).GetMethod("Increment", new[] {typeof (long).MakeByRefType()}); // The parameter of the finally built lambda. var parameter = Expression.Parameter(typeof (object)); // Cast the parameter into the target type. var targetInstance = Expression.TypeAs(parameter, targetType); // Access the target field of the target instance, and pass it as parameter to the call of Interlocked.Increment. var call = Expression.Call(increaseMethod, Expression.Field(targetInstance, targetField)); // Build the lambda. var lambda = Expression.Lambda(typeof (Action<object>), call, parameter); // Compile into delegate. var func = (Action<object>)lambda.Compile(); // Call the compiled delegate. func(instance); } }
这样做必定会有一些性能损失,但是如果lambda的Compile与调用相比少得多的话,那么影响应该不至于十分显著;更何况,原本都已经使用了反射了。
posted on 2012-01-29 22:40 Gildor Wang 阅读(531) 评论(0) 编辑 收藏 举报