带枚举的Interlocked.CompareExchange

我正在尝试使用此枚举的Interlocked.CompareExchange

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

以下代码无法编译,但这就是我想要做的:

 if (Interlocked.CompareExchange(ref state, State.Running, State.Idle) != State.Idle) { throw new InvalidOperationException("Unable to run - not idle"); } 

当然我可以使用int而不是枚举并使用属性:

 private int state = (int)State.Idle; public State { get { return (State)state; } } 

然后将枚举转换为int:

 if (Interlocked.CompareExchange(ref state, (int)State.Running, (int)State.Idle) != (int)State.Idle) { throw new InvalidOperationException("Unable to run - not idle"); } 

但有没有更好的方法来做到这一点?

为简单起见,没有:-)

可悲的是,C#/ .NET将enum视为完整类型,部分与其基类型断开连接。 每当你尝试在enum上做一些“幻想”时,你会遇到一些障碍。

它可以从IL中获得,并且可以为此创建一个可以从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); } } 

输出:

 x:Y
 y:X

这只是将参数转发给正确的Interlocked.Exchange重载。 如果T不是真正的枚举类型,或者它的基础类型没有Interlocked.Exchange重载,它会严重失败。

生成的IL是可validation的,至少根据PEVerify,可以通过使用AssemblyBuilder并将结果保存到文件来检查。

但有没有更好的方法来做到这一点?

我使用一个类而不是枚举:

 public class DataCollectionManagerState { public static readonly DataCollectionManagerState Off = new DataCollectionManagerState() { }; public static readonly DataCollectionManagerState Starting = new DataCollectionManagerState() { }; public static readonly DataCollectionManagerState On = new DataCollectionManagerState() { }; private DataCollectionManagerState() { } public override string ToString() { if (this == Off) return "Off"; if (this == Starting) return "Starting"; if (this == On) return "On"; throw new Exception(); } } public class DataCollectionManager { private static DataCollectionManagerState _state = DataCollectionManagerState.Off; public static void StartDataCollectionManager() { var originalValue = Interlocked.CompareExchange(ref _state, DataCollectionManagerState.Starting, DataCollectionManagerState.Off); if (originalValue != DataCollectionManagerState.Off) { throw new InvalidOperationException(string.Format("StartDataCollectionManager can be called when it's state is Off only. Current state is \"{0}\".", originalValue.ToString())); } // Start Data Collection Manager ... originalValue = Interlocked.CompareExchange(ref _state, DataCollectionManagerState.On, DataCollectionManagerState.Starting); if (originalValue != DataCollectionManagerState.Starting) { // Your code is really messy throw new Exception(string.Format("Unexpected error occurred. Current state is \"{0}\".", originalValue.ToString())); } } } 

枚举上的Interlocked操作没有问题:

 public enum State { Idle, Running } unsafe State CompareExchange(ref State target, State v, State cmp) { fixed (State* p = &target) return (State)Interlocked.CompareExchange(ref *(int*)p, (int)v, (int)cmp); } 

请参阅https://stackoverflow.com/a/5589515/147511上的完整答案和讨论