Interlocked.CompareExchange with enum

前端 未结 5 1740
北海茫月
北海茫月 2020-12-31 05:15

I\'m trying to use Interlocked.CompareExchange with this enum:

public enum State {
    Idle,
    Running,
    //...
}

The following code do

5条回答
  •  攒了一身酷
    2020-12-31 05:41

    It's possible from IL, and it's possible to create a helper method for this that can be used from C#.

    using System;
    using System.Reflection;
    using System.Reflection.Emit;
    using System.Threading;
    
    static class CompareExchangeEnumImpl
    {
        public delegate T dImpl(ref T location, T value, T comparand);
        public static readonly dImpl Impl = CreateCompareExchangeImpl();
    
        static dImpl CreateCompareExchangeImpl()
        {
            var underlyingType = Enum.GetUnderlyingType(typeof(T));
            var dynamicMethod = new DynamicMethod(string.Empty, typeof(T), new[] { typeof(T).MakeByRefType(), typeof(T), typeof(T) });
            var ilGenerator = dynamicMethod.GetILGenerator();
            ilGenerator.Emit(OpCodes.Ldarg_0);
            ilGenerator.Emit(OpCodes.Ldarg_1);
            ilGenerator.Emit(OpCodes.Ldarg_2);
            ilGenerator.Emit(
                OpCodes.Call,
                typeof(Interlocked).GetMethod(
                    "CompareExchange",
                    BindingFlags.Static | BindingFlags.Public,
                    null,
                    new[] { underlyingType.MakeByRefType(), underlyingType, underlyingType },
                    null));
            ilGenerator.Emit(OpCodes.Ret);
            return (dImpl)dynamicMethod.CreateDelegate(typeof(dImpl));
        }
    }
    
    public static class InterlockedEx
    {
        public static T CompareExchangeEnum(ref T location, T value, T comparand)
        {
            return CompareExchangeEnumImpl.Impl(ref location, value, comparand);
        }
    }
    
    public enum Foo
    {
        X,
        Y,
    }
    
    static class Program
    {
        static void Main()
        {
            Foo x = Foo.X;
            Foo y = Foo.Y;
            y = InterlockedEx.CompareExchangeEnum(ref x, y, Foo.X);
            Console.WriteLine("x: " + x);
            Console.WriteLine("y: " + y);
        }
    }
    

    Output:

    x: Y
    y: X
    

    This just forwards the arguments to the correct Interlocked.Exchange overload. It fails badly if T isn't really an enum type, or its underlying type doesn't have an Interlocked.Exchange overload.

    The generated IL is verifiable, at least according to PEVerify, as can be checked by making this use AssemblyBuilder and saving the result to a file.

提交回复
热议问题