我正在尝试使用Interlocked.CompareExchange这个枚举:
public enum State {
Idle,
Running,
//...
}
Run Code Online (Sandbox Code Playgroud)
以下代码无法编译,但这就是我想要做的:
if (Interlocked.CompareExchange(ref state, State.Running, State.Idle) != State.Idle) {
throw new InvalidOperationException("Unable to run - not idle");
}
Run Code Online (Sandbox Code Playgroud)
当然我可以使用int而不是枚举并使用属性:
private int state = (int)State.Idle;
public State { get { return (State)state; } }
Run Code Online (Sandbox Code Playgroud)
然后将枚举转换为int:
if (Interlocked.CompareExchange(ref state, (int)State.Running, (int)State.Idle) != (int)State.Idle) {
throw new InvalidOperationException("Unable to run - not idle");
}
Run Code Online (Sandbox Code Playgroud)
但有没有更好的方法来做到这一点?
小智 18
IL可以实现,并且可以为此创建一个可以从C#中使用的辅助方法.
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
static class CompareExchangeEnumImpl<T>
{
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<T>(ref T location, T value, T comparand)
{
return CompareExchangeEnumImpl<T>.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);
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
x: Y y: X
这只是将参数转发给正确的Interlocked.Exchange重载.如果T它不是真正的枚举类型,或者它的基础类型没有Interlocked.Exchange重载,它会严重失败.
生成的IL是可验证的,至少根据PEVerify,可以通过使用AssemblyBuilder并将结果保存到文件来检查.
小智 5
但是,有更好的方法吗?
我使用一个类而不是枚举:
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()));
}
}
}
Run Code Online (Sandbox Code Playgroud)
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);
}
Run Code Online (Sandbox Code Playgroud)
在/sf/answers/391266081/上查看我的完整答案和讨论
| 归档时间: |
|
| 查看次数: |
2577 次 |
| 最近记录: |