Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/267.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/asp.net-mvc-3/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Interlocked.CompareExchange与枚举_C#_Interlocked - Fatal编程技术网

C# Interlocked.CompareExchange与枚举

C# Interlocked.CompareExchange与枚举,c#,interlocked,C#,Interlocked,我正在尝试使用此枚举: public enum State { Idle, Running, //... } 以下代码未编译,但这正是我想要做的: if (Interlocked.CompareExchange(ref state, State.Running, State.Idle) != State.Idle) { throw new InvalidOperationException("Unable to run - not idle"); } 确定我可

我正在尝试使用此枚举:

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而不是enum并使用属性:

private int state = (int)State.Idle;
public State { get { return (State)state; } }
然后将枚举强制转换为整数:

if (Interlocked.CompareExchange(ref state, (int)State.Running, (int)State.Idle) !=  (int)State.Idle) {
    throw new InvalidOperationException("Unable to run - not idle");
}
但是有没有更好的方法可以做到这一点呢?

简单点说,没有:-)


遗憾的是,C.Y./.NET考虑<代码> EnUM < /代码> S作为完全类型,部分与它们的基类型断开连接。每次你试图在

枚举上做一些“花哨”的事情时,你都会遇到一些障碍。

可以从IL开始,也可以为此创建一个可以从C#使用的助手方法

使用系统;
运用系统反思;
使用System.Reflection.Emit;
使用系统线程;
静态类CompareExchangeEnumImpl
{
公共代表T dImpl(参考T位置、T值、T比较);
public static readonly dImpl Impl=createCompareeExchangeImpl();
静态dImpl CreateCompareExchangeImpl()
{
var underyingtype=Enum.getunderyingtype(typeof(T));
var dynamicMethod=new dynamicMethod(string.Empty,typeof(T),new[]{typeof(T).MakeByRefType(),typeof(T),typeof(T)});
var ilGenerator=dynamicMethod.GetILGenerator();
ilGenerator.Emit(操作码.Ldarg_0);
ilGenerator.Emit(操作码.Ldarg_1);
ilGenerator.Emit(操作码.Ldarg_2);
ilGenerator.Emit(
操作码,呼叫,
typeof(联锁).GetMethod(
“比较交换”,
BindingFlags.Static | BindingFlags.Public,
无效的
新[]{underyingType.MakeByRefType(),underyingType,underyingType},
空);
ilGenerator.Emit(操作码.Ret);
return(dImpl)dynamicMethod.CreateDelegate(typeof(dImpl));
}
}
公共静态类Interlocatex
{
公共静态T比较交换枚举(参考T位置、T值、T比较对象)
{
返回CompareExchangeEnumImpl.Impl(参考位置、值、比较对象);
}
}
公众谘询委员会
{
X,,
Y
}
静态类程序
{
静态void Main()
{
Foo x=Foo.x;
Foo y=Foo.y;
y=InterlockedEx.CompareExchangeEnum(参考x,y,Foo.x);
控制台写入线(“x:+x”);
控制台写入线(“y:+y”);
}
}
输出:

x: Y y: X x:Y y:X 这只是将参数转发到正确的
联锁.Exchange
重载。如果
T
不是一个真正的枚举类型,或者它的基础类型没有
联锁的.Exchange
重载,那么它会严重失败

至少根据PEVerify,生成的IL是可验证的,这可以通过使用
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()));
        }
    }
}

联锁
枚举的操作没有问题:

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);
}
使用
System.Runtime.CompilerServices.Unsafe查看我在

上的完整答案和讨论 这是一个非常好的深入的相关答案

使用系统;
使用System.Runtime.CompilerServices;
使用系统线程;
公共静态类Interlocatex
{
/// 
///与的枚举等价
/// 
公共静态TEnum比较交换(参考TEnum位置、TEnum值、TEnum比较)
其中TEnum:struct,Enum
{
返回不安全的.SizeOf()开关
{
//.NET不支持1字节和2字节的原子操作
//没有通用的硬件支持。
4=>CompareExchange32位(参考位置、值、比较项),
8=>CompareExchange64位(参考位置、值、比较项),
_=>抛出新的NotSupportedException(“只有基础类型为4字节或8字节的枚举才允许与Interlocked一起使用”)
};
静态TEnum CompareExchange 32位(参考TEnum位置、TEnum值、TEnum CompareAnd)
{
int COMARANDRAW=不安全的.As(参考COMARAND);
int valueRaw=不安全的.As(参考值);
ref int locationRaw=参考不安全As(参考位置);
int returnRaw=联锁的比较交换(参考位置RAW、值RAW、比较RAW);
返回不安全。As(参考returnRaw);
}
静态TEnum CompareExchange64位(参考TEnum位置、TEnum值、TEnum CompareAnd)
{
长比较RAW=不安全的As(参考比较);
长值原始=不安全的.As(参考值);
ref long locationRaw=参考不安全As(参考位置);
长返回原始=联锁。比较交换(参考位置原始、值原始、比较原始);
返回不安全。As(参考returnRaw);
}
}
}

你所展示的(将其视为
int
和施法)基本上就是我所做的。@MarcGravel:基本上?老实说,这没什么大不了的。就我个人而言,我只会将其保留为
enum
,但在您进行交换之前将其强制转换。@James您不能这样做;该字段必须为
int
,才能在调用中用作
ref
。你不能在
ref
@James:这会扼杀使用
compareeexchange
@DarthVader的理由为什么不呢?我从来没有理由使用Enum的
Interlocked.Exchange
,但我确实有其他情况,在这些情况下,CIL允许使用一种清晰正确的方法来做某事,但C#不允许。在这种情况下,我认为C#不是做这项工作的合适工具,所以我不使用C#。真淘气!但是我喜欢它,并将它直接复制到我的代码中:)现在,你也可以做一个
线程。volatieread(myEnum)
?@EugeneBeresovsky当然,我不明白为什么不。我的回答应该很容易适应这个问题。还有很多其他方法也可以添加,我认为将所有这些方法都包含在答案中不会有任何好处。:)如果enum作为一种策略对象控制很多事情,这通常是一个好主意。一个症状是不同pla中枚举上有很多开关
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);
}