怎样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与调用相比少得多的话,那么影响应该不至于十分显著;更何况,原本都已经使用了反射了。

你可能感兴趣的:(Field)