Как применять InterLocked.Exchange для типов Enum в С#?
public enum MyEnum{Value1, Value2}
class MyClass
{
private MyEnum _field;
public MyEnum Field // added for convenience
{
get { return _field; }
set { Interlocked.Exchange(ref _field, value); // ERROR CS0452 }
}
}
можно решить с помощью:
public enum MyEnum{Value1, Value2}
public class MyClass2
{
private int _field; //change to int
public MyEnum Field // added for convenience
{
get { return (MyEnum)_field; }
set { System.Threading.Interlocked.Exchange(ref _field, (int)value); }
}
}
Есть ли лучший способ для этой проблемы?
Ответы
Ответ 1
Есть ли лучший способ для этой проблемы?
Если вам нужно использовать Interlocked.Exchange
, тогда это лучший способ, на самом деле я думаю, что это единственный способ обмена перечислением.
Причина, по которой вы получаете ошибку компилятора, заключается в том, что компилятор считает, что вы хотите использовать Exchange<T>
, но T должен быть ссылочным типом для этого, поскольку вы не используете ссылочный тип, который он терпит неудачу. Таким образом, лучшая работа заключается в том, чтобы выполнить int
, как вы это сделали, и, таким образом, заставить компилятор использовать не-общий Exchange(int, int)
.
Ответ 2
Кажется, вам не нужна функция "exchange" Interlocked.Exchange, поскольку вы игнорируете ее возвращаемое значение. Поэтому я думаю, что решение, которое может сделать вас счастливее, - отметить значение _field как изменчивое:
private volatile MyEnum _field;
Ответ 3
Способы Interlocked
прекрасны. Вы можете использовать простой старый lock
, но это кажется излишним. Тем не менее, вам понадобится использовать защищенное чтение какого-либо типа в геттере, иначе вы можете столкнуться с проблемами защиты памяти. Поскольку вы уже используете метод Interlocked
в сеттере, имеет смысл сделать то же самое в getter.
public MyEnum Field // added for convenience
{
get { return (MyEnum)Interlocked.CompareExchange(ref _field, 0, 0); }
set { Interlocked.Exchange(ref _field, (int)value); }
}
Вы также можете уйти с пометкой поля как volatile
, если хотите.
Ответ 4
Почему бы просто не синхронизировать потоки?
protected static object _lockObj = new object();
set
{
lock(_lockObj)
{
_field = value;
}
}
Ответ 5
Есть ли лучший способ для этой проблемы?
Я использую класс вместо Enum:
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 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()));
}
}
}