"Decimal" 标志(而不是二进制)- 内置方式?
"Decimal" flags (instead of binary) - built-in way?
我有这个从一些文档中得到的公式,它应该解释“Flags”整数代表什么:
Flags = Spectator * 1 + TemporarySpectator * 10 + PureSpectator * 100 + AutoTarget * 1000 + CurrentTargetId * 10000
我写了这段代码,它能够将一个数字(标志)转换为一些布尔值 + 一个整数,就像在公式中一样:
////////// From Values to Flag //////////
bool Spectator = true;
bool TemporarySpectator = false;
bool PureSpectator = true;
bool AutoTarget = false;
int CurrentTargetId = 255;
int Calculate() =>
(Convert.ToInt32(Spectator) * 1) +
(Convert.ToInt32(TemporarySpectator) * 10) +
(Convert.ToInt32(PureSpectator) * 100) +
(Convert.ToInt32(AutoTarget) * 1000) +
(Convert.ToInt32(CurrentTargetId) * 10000);
int Result = Calculate(); // 2550101
////////// From Flag to Values //////////
CurrentTargetId = Convert.ToInt32(Result / 10000);
AutoTarget = Convert.ToBoolean((Result - (CurrentTargetId * 10000)) / 1000);
PureSpectator = Convert.ToBoolean((Result - (CurrentTargetId * 10000) - (Convert.ToInt32(AutoTarget) * 1000)) / 100);
TemporarySpectator = Convert.ToBoolean((Result - (CurrentTargetId * 10000) - (Convert.ToInt32(AutoTarget) * 1000) - (Convert.ToInt32(PureSpectator) * 100)) / 10);
Spectator = Convert.ToBoolean((Result - (CurrentTargetId * 10000) - (Convert.ToInt32(AutoTarget) * 1000) - (Convert.ToInt32(PureSpectator) * 100) - (Convert.ToInt32(TemporarySpectator) * 100)) / 1);
Result = Calculate(); // 2550101
如您所见,我的代码也可以执行反向操作 - 将值转换为标志。
Fiddle 我的代码:https://dotnetfiddle.net/ua2wi8
这种操作有什么名字吗?我知道枚举的 FlagsAttribute
类似,但将标志存储为单个位(“二进制数字”)而不是像我的情况那样的十进制数字。
在 C# 中是否有更简单甚至本机的方法来执行此操作?使用模型 class 中的布尔值会更好。
FlagsAttribute
for enums 仅控制使用 ToString
时枚举的打印方式,它将在漂亮的 comma-separated 列表中列出所有值。
您尝试做的事情可以通过枚举(最佳)实现,但如果您了解二进制或 2 的幂,也可以通过使用整数来实现。
使用枚举
public enum MyEnum {
Spectator = 1 << 0, // 1 or 2^0
TemporarySpectator = 1 << 1, // 2 or 2^1
PureSpectator = 1 << 2, // 4 or 2^2
AutoTarget = 1 << 3, // 8 or 2^3
CurrentTargetId = 1 << 4 // 16 or 2^4
}
MyEnum flags = MyEnum.Spectator | MyEnum.PureSpectator | MyEnum.CurrentTargetId;
////////// Extracting individual values from flags //////////
MyEnum currentTargetId = flags & MyEnum.CurrentTargetId;
MyEnum autoTarget = flags & MyEnum.AutoTarget;
MyEnum pureSpectator = flags & MyEnum.PureSpectator;
MyEnum temporarySpectator = flags & MyEnum.TemporarySpectator;
MyEnum spectator = flags & MyEnum.Spectator;
if (flags.HasFlag(MyEnum.AutoTarget))
{
// Do Stuff
}
if (flags.HasFlag(MyEnum.Spectator))
{
// Do Stuff
}
使用纯整数
const int spectator = 0b_0000_0001; // 1 or 2^0
const int temporarySpectator = 0b_0000_0010; // 2 or 2^1
const int pureSpectator = 0b_0000_0100; // 4 or 2^2
const int autoTarget = 0b_0000_1000; // 8 or 2^3
const int currentTargetId = 0b_0001_0000; // 16 or 2^4
int flags = spectator | pureSpectator | currentTargetId;
////////// Extracting individual values from flags //////////
int currentTargetIdVal = flags & currentTargetId;
int autoTargetVal = flags & autoTarget;
int pureSpectatorVal = flags & pureSpectator;
int temporarySpectatorVal = flags & temporarySpectator;
int spectatorVal = flags & spectator;
if ((flags & autoTarget) > 0)
{
// Do Stuff
}
if ((flags & spectator) > 0)
{
// Do Stuff
}
C#也允许使用Binary Literals,所以MyEnum
也可以这样写:
public enum MyEnum {
Spectator = 0b_0000_0001,
TemporarySpectator = 0b_0000_0010,
PureSpectator = 0b_0000_0100,
AutoTarget = 0b_0000_1000,
CurrentTargetId = 0b_0001_0000
}
您可以将其全部包装在 class 中,并使用模运算符来简化事情:
class DecimalFlags
{
private int _value = 0;
public bool Spectator
{
get
{
return _value % 10 == 1;
}
set
{
if (value && _value % 10 != 1) _value += 1;
else if (_value % 10 == 1) _value -= 1;
}
}
public bool TemporarySpectator
{
get
{
return (_value/10) % 10 == 1;
}
set
{
if (value && (_value/10) % 10 != 1) _value += 10;
else if ((_value/10) % 10 == 1) _value -= 10;
}
}
public bool PureSpectator
{
get
{
return (_value / 100) % 10 == 1;
}
set
{
if (value && (_value / 100) % 10 != 1) _value += 100;
else if ((_value / 100) % 10 == 1) _value -= 100;
}
}
public bool AutoTarget
{
get
{
return (_value / 1000) % 10 == 1;
}
set
{
if (value && (_value / 1000) % 10 != 1) _value += 1000;
else if ((_value / 1000) % 10 == 1) _value -= 1000;
}
}
public int CurrentTargetId {get;set;}
public static explicit operator int(DecimalFlags df)
{
return df._value + df.CurrentTargetId*10000;
}
public static explicit operator DecimalFlags(int i)
{
var df = new DecimalFlags();
df.CurrentTargetId = i/10000;
df._value = i - df.CurrentTargetId*10000;
return df;
}
}
然后当你需要将你的值传回游戏时你可以说
MyGameFunction((int)decimalFlags);
或者从游戏中获取值:
int value = GetValueFromGame();
var decimalFlags = (DecimalFlags)value;
bool isSpectator = decimalFlags.Spectator; //etc
您可以创建自己的结构对象来表示标志。下面显示的这个结构将以 8 个字节或 long
的大小存储所有标志 和 您的 CurrentTaregtId
。然后,您可以使用 .Calculate
函数来获取您要查找的十进制值。所有这些标志的值都可以达到 255
,而十进制系统的缺点是每个值最多只能容纳 9
才能用作适当的 'flag'.
请记住,此代码尚未测试。因此可能需要 较小的 改动。
[StructLayout(LayoutKind.Explicit)]
public struct MyFlags {
[FieldOffset(0)]
public ulong Value;
[FieldOffset(0)]
public byte Spectator;
[FieldOffset(1)]
public byte TemporarySpectator;
[FieldOffset(2)]
public byte PureSpectator;
[FieldOffset(3)]
public byte AutoTarget;
[FieldOffset(4)]
public uint CurrentTargetId;
public MyFlags(in ulong value) {
Value = value;
}
public MyFlags(in byte spectator, in byte temporarySpectator, in byte pureSpectator, in byte autoTarget, in uint currentTargetId) {
Spectator = spectator;
TemporarySpectator = temporarySpectator;
PureSpectator = pureSpectator;
AutoTarget = autoTarget;
CurrentTargetId = currentTargetId;
}
public long Calculate()
{
return Spectator
+ TemporarySpectator * 10
+ PureSpectator * 100
+ AutoTarget * 1000
+ CurrentTargetId * 10000;
}
}
/// Usage
MyFlags flags = new MyFlags();
flags.TemporarySpectator = 4;
flags.PureSpectator = 7;
flags.CurrentTargetId = 255;
flags.Calculate();
if (flags.AutoTarget > 0)
{
// Do stuff
}
以下是我最终的做法,使用手动字符串解析:
public class SpectatorStatus : IFlag
{
public SpectatorStatus(int value)
{
var valueStr = value.ToString("D7");
const char True = '1';
CurrentTargetId = Convert.ToInt32(valueStr[..3]);
AutoTarget = valueStr[3] == True;
PureSpectator = valueStr[4] == True;
TemporarySpectator = valueStr[5] == True;
Spectator = valueStr[6] == True;
}
public bool Spectator { get; }
public bool TemporarySpectator { get; }
public bool PureSpectator { get; }
public bool AutoTarget { get; }
public int CurrentTargetId { get; }
public override string ToString()
=> (Convert.ToInt32(Spectator)
+ Convert.ToInt32(TemporarySpectator) * 10
+ Convert.ToInt32(PureSpectator) * 100
+ Convert.ToInt32(AutoTarget) * 1000
+ CurrentTargetId * 10000)
.ToString("D7");
}
我有这个从一些文档中得到的公式,它应该解释“Flags”整数代表什么:
Flags = Spectator * 1 + TemporarySpectator * 10 + PureSpectator * 100 + AutoTarget * 1000 + CurrentTargetId * 10000
我写了这段代码,它能够将一个数字(标志)转换为一些布尔值 + 一个整数,就像在公式中一样:
////////// From Values to Flag //////////
bool Spectator = true;
bool TemporarySpectator = false;
bool PureSpectator = true;
bool AutoTarget = false;
int CurrentTargetId = 255;
int Calculate() =>
(Convert.ToInt32(Spectator) * 1) +
(Convert.ToInt32(TemporarySpectator) * 10) +
(Convert.ToInt32(PureSpectator) * 100) +
(Convert.ToInt32(AutoTarget) * 1000) +
(Convert.ToInt32(CurrentTargetId) * 10000);
int Result = Calculate(); // 2550101
////////// From Flag to Values //////////
CurrentTargetId = Convert.ToInt32(Result / 10000);
AutoTarget = Convert.ToBoolean((Result - (CurrentTargetId * 10000)) / 1000);
PureSpectator = Convert.ToBoolean((Result - (CurrentTargetId * 10000) - (Convert.ToInt32(AutoTarget) * 1000)) / 100);
TemporarySpectator = Convert.ToBoolean((Result - (CurrentTargetId * 10000) - (Convert.ToInt32(AutoTarget) * 1000) - (Convert.ToInt32(PureSpectator) * 100)) / 10);
Spectator = Convert.ToBoolean((Result - (CurrentTargetId * 10000) - (Convert.ToInt32(AutoTarget) * 1000) - (Convert.ToInt32(PureSpectator) * 100) - (Convert.ToInt32(TemporarySpectator) * 100)) / 1);
Result = Calculate(); // 2550101
如您所见,我的代码也可以执行反向操作 - 将值转换为标志。
Fiddle 我的代码:https://dotnetfiddle.net/ua2wi8
这种操作有什么名字吗?我知道枚举的 FlagsAttribute
类似,但将标志存储为单个位(“二进制数字”)而不是像我的情况那样的十进制数字。
在 C# 中是否有更简单甚至本机的方法来执行此操作?使用模型 class 中的布尔值会更好。
FlagsAttribute
for enums 仅控制使用 ToString
时枚举的打印方式,它将在漂亮的 comma-separated 列表中列出所有值。
您尝试做的事情可以通过枚举(最佳)实现,但如果您了解二进制或 2 的幂,也可以通过使用整数来实现。
使用枚举
public enum MyEnum {
Spectator = 1 << 0, // 1 or 2^0
TemporarySpectator = 1 << 1, // 2 or 2^1
PureSpectator = 1 << 2, // 4 or 2^2
AutoTarget = 1 << 3, // 8 or 2^3
CurrentTargetId = 1 << 4 // 16 or 2^4
}
MyEnum flags = MyEnum.Spectator | MyEnum.PureSpectator | MyEnum.CurrentTargetId;
////////// Extracting individual values from flags //////////
MyEnum currentTargetId = flags & MyEnum.CurrentTargetId;
MyEnum autoTarget = flags & MyEnum.AutoTarget;
MyEnum pureSpectator = flags & MyEnum.PureSpectator;
MyEnum temporarySpectator = flags & MyEnum.TemporarySpectator;
MyEnum spectator = flags & MyEnum.Spectator;
if (flags.HasFlag(MyEnum.AutoTarget))
{
// Do Stuff
}
if (flags.HasFlag(MyEnum.Spectator))
{
// Do Stuff
}
使用纯整数
const int spectator = 0b_0000_0001; // 1 or 2^0
const int temporarySpectator = 0b_0000_0010; // 2 or 2^1
const int pureSpectator = 0b_0000_0100; // 4 or 2^2
const int autoTarget = 0b_0000_1000; // 8 or 2^3
const int currentTargetId = 0b_0001_0000; // 16 or 2^4
int flags = spectator | pureSpectator | currentTargetId;
////////// Extracting individual values from flags //////////
int currentTargetIdVal = flags & currentTargetId;
int autoTargetVal = flags & autoTarget;
int pureSpectatorVal = flags & pureSpectator;
int temporarySpectatorVal = flags & temporarySpectator;
int spectatorVal = flags & spectator;
if ((flags & autoTarget) > 0)
{
// Do Stuff
}
if ((flags & spectator) > 0)
{
// Do Stuff
}
C#也允许使用Binary Literals,所以MyEnum
也可以这样写:
public enum MyEnum {
Spectator = 0b_0000_0001,
TemporarySpectator = 0b_0000_0010,
PureSpectator = 0b_0000_0100,
AutoTarget = 0b_0000_1000,
CurrentTargetId = 0b_0001_0000
}
您可以将其全部包装在 class 中,并使用模运算符来简化事情:
class DecimalFlags
{
private int _value = 0;
public bool Spectator
{
get
{
return _value % 10 == 1;
}
set
{
if (value && _value % 10 != 1) _value += 1;
else if (_value % 10 == 1) _value -= 1;
}
}
public bool TemporarySpectator
{
get
{
return (_value/10) % 10 == 1;
}
set
{
if (value && (_value/10) % 10 != 1) _value += 10;
else if ((_value/10) % 10 == 1) _value -= 10;
}
}
public bool PureSpectator
{
get
{
return (_value / 100) % 10 == 1;
}
set
{
if (value && (_value / 100) % 10 != 1) _value += 100;
else if ((_value / 100) % 10 == 1) _value -= 100;
}
}
public bool AutoTarget
{
get
{
return (_value / 1000) % 10 == 1;
}
set
{
if (value && (_value / 1000) % 10 != 1) _value += 1000;
else if ((_value / 1000) % 10 == 1) _value -= 1000;
}
}
public int CurrentTargetId {get;set;}
public static explicit operator int(DecimalFlags df)
{
return df._value + df.CurrentTargetId*10000;
}
public static explicit operator DecimalFlags(int i)
{
var df = new DecimalFlags();
df.CurrentTargetId = i/10000;
df._value = i - df.CurrentTargetId*10000;
return df;
}
}
然后当你需要将你的值传回游戏时你可以说
MyGameFunction((int)decimalFlags);
或者从游戏中获取值:
int value = GetValueFromGame();
var decimalFlags = (DecimalFlags)value;
bool isSpectator = decimalFlags.Spectator; //etc
您可以创建自己的结构对象来表示标志。下面显示的这个结构将以 8 个字节或 long
的大小存储所有标志 和 您的 CurrentTaregtId
。然后,您可以使用 .Calculate
函数来获取您要查找的十进制值。所有这些标志的值都可以达到 255
,而十进制系统的缺点是每个值最多只能容纳 9
才能用作适当的 'flag'.
请记住,此代码尚未测试。因此可能需要 较小的 改动。
[StructLayout(LayoutKind.Explicit)]
public struct MyFlags {
[FieldOffset(0)]
public ulong Value;
[FieldOffset(0)]
public byte Spectator;
[FieldOffset(1)]
public byte TemporarySpectator;
[FieldOffset(2)]
public byte PureSpectator;
[FieldOffset(3)]
public byte AutoTarget;
[FieldOffset(4)]
public uint CurrentTargetId;
public MyFlags(in ulong value) {
Value = value;
}
public MyFlags(in byte spectator, in byte temporarySpectator, in byte pureSpectator, in byte autoTarget, in uint currentTargetId) {
Spectator = spectator;
TemporarySpectator = temporarySpectator;
PureSpectator = pureSpectator;
AutoTarget = autoTarget;
CurrentTargetId = currentTargetId;
}
public long Calculate()
{
return Spectator
+ TemporarySpectator * 10
+ PureSpectator * 100
+ AutoTarget * 1000
+ CurrentTargetId * 10000;
}
}
/// Usage
MyFlags flags = new MyFlags();
flags.TemporarySpectator = 4;
flags.PureSpectator = 7;
flags.CurrentTargetId = 255;
flags.Calculate();
if (flags.AutoTarget > 0)
{
// Do stuff
}
以下是我最终的做法,使用手动字符串解析:
public class SpectatorStatus : IFlag
{
public SpectatorStatus(int value)
{
var valueStr = value.ToString("D7");
const char True = '1';
CurrentTargetId = Convert.ToInt32(valueStr[..3]);
AutoTarget = valueStr[3] == True;
PureSpectator = valueStr[4] == True;
TemporarySpectator = valueStr[5] == True;
Spectator = valueStr[6] == True;
}
public bool Spectator { get; }
public bool TemporarySpectator { get; }
public bool PureSpectator { get; }
public bool AutoTarget { get; }
public int CurrentTargetId { get; }
public override string ToString()
=> (Convert.ToInt32(Spectator)
+ Convert.ToInt32(TemporarySpectator) * 10
+ Convert.ToInt32(PureSpectator) * 100
+ Convert.ToInt32(AutoTarget) * 1000
+ CurrentTargetId * 10000)
.ToString("D7");
}