簡體   English   中英

“十進制”標志(而不是二進制)-內置方式?

[英]"Decimal" flags (instead of binary) - built-in way?

我有這個從一些文檔中得到的公式,它應該解釋“標志”integer 代表什么:

Flags = Spectator * 1 + TemporarySpectator * 10 + PureSpectator * 100 + AutoTarget * 1000 + CurrentTargetId * 10000

我寫了這段代碼,它能夠將一個數字(標志)轉換為一些布爾值 + 一個 integer,就像在公式中一樣:

////////// 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

如您所見,我的代碼也能夠執行反向操作 - 將值轉換為標志。

我的代碼小提琴: https://do.netfiddle.net/ua2wi8

這種操作有什么名字嗎? 我知道枚舉的FlagsAttribute類似但將標志存儲為單個位(“二進制數字”)而不是像我的情況那樣的十進制數字。

在 C# 中有沒有更簡單甚至是原生的方法來做到這一點? 使用來自 model class 的布爾值將是一個加號。

枚舉的FlagsAttribute僅控制使用ToString時枚舉的打印方式,並且它將在一個漂亮的逗號分隔列表中列出所有值。

您嘗試做的事情可以通過枚舉(最佳)實現,但如果您了解二進制或 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

您可以創建自己的結構 object 來表示標志。 下面顯示的這個結構將以 8 個字節或long的大小存儲所有標志CurrentTaregtId 然后,您可以使用.Calculate function 獲取您要查找的十進制值。 所有這些標志的值都可以達到255 ,而十進制系統的缺點是每個值最多只能容納9才能用作適當的“標志”。

請記住,此代碼尚未經過測試 因此可能需要進行微小的改動。

[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");
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM