如何使用位/位运算符来控制对象状态?

6

我希望创建一个轻量级的对象数据包,在客户端和服务器应用程序之间传递。

这是一个非常简单的任务,只需要使用1字节就可以控制,因此每个字节中的每一位都有不同的含义,

仅使用位操作即可实现。

0 = False 
1 = True

我现在需要的物品:

1 - Loaded from database 
2 - Persisted
3 - Changed
4 - Marked to Delete
5 -
6 - 
7 - Null Value 
8 - Read Only


1) How do I use bit operators in Delphi to check each bit value? 
2) How do I set the bit Values?

解决方案

在得到所有帮助之后,我将使用下一个Set。

  TStateType = (
    stLoaded    = 0,   // loaded from persistance
    stNative    = 2,   // value loaded and converted to native type
    stPersisted = 3,   // saved
    stChanged   = 4,   // object or member changed
    stToDelete  = 5,   // marked to delete
    stReadOnly  = 6,   // read only object, will not allow changes
    stNull      = 7    // value is null
  );
  TState = Set of TStateType;

对于流 -> 持久化,将使用此记录:

  TDataPackage = record
    Data: TBytes;
    TypeInfo: TMetaInfo;
    State: Byte;
    Instance: TBuffer;
  end;

感谢大家提供的答案和评论。


1
集合是比直接使用位运算符更好的技术。它们并不比位运算符“重”,因为集合操作编译成与二进制操作相同的机器代码。优点是它们是类型安全的,并由编译器检查,而位运算都是在整数上进行的。 - Barry Kelly
1
所以,如果您有一个最多可以拥有8个成员的集合,则其类型大小将为1字节。 - Barry Kelly
我在这里问了一个相关的问题:“对于多个布尔值,使用数组还是位访问更快? - Tom A
3个回答

8

我真的很想使用一个“set”来完成这项任务。然而,我注意到你确实需要一个“byte”。那么最好在所有地方都使用“set”,然后在最后将类型转换为“byte”。

这个解决方案将需要更少的输入,支持标准的Delphi运算符,并且像Barry Kelly指出的那样,基本没有性能损失。

procedure Test;
type
  TSetValues = (
    TSetValue1   = 0,
    TSetValue2   = 1,
    TSetValue4   = 2,
    TSetValue8   = 3,
    TSetValue16  = 4,
    TSetValue32  = 5,
    TSetValue64  = 6,
    TSetValue128 = 7
  );

  TMySet = set of TSetValues;
var
  myValue: byte;
  mySet: TMySet;
begin
  mySet := [TSetValue2, TSetValue16, TSetValue128];
  myValue := byte(mySet);
  ShowMessage(IntToStr(myValue)); // <-- shows 146
end;

这是获胜者,我认为进行一些重构将有助于改进我的现有代码。 - Cesar Romero

6
我会使用一个集合(set)来解决这个问题:
type
    TMyDatum = (mdLoaded, mdPersisted, mdChanged, mdMarkedToDelete, ...);
    TMyData = set of TMyDatum;

var
  Foo: TMyData;
begin 
  Foo := [mdLoaded, mdChanged];
  if (mdPersisted in Foo) then ...

这些被实现为整数,所以你可以轻松地传递它们。我发现这段代码比按位运算符可读性更高。


@Craig,这是我今天的代码。正如我在问题中指出的那样,它必须更轻便,而且我不确定枚举类型的大小。此外,流式传输1字节比流式传输枚举类型更容易。测试位值的代码将嵌入到TStatus对象中,例如Status.IsLoaded。 - Cesar Romero
1
Cesar- 一个集合的大小为1、2、3、4字节,具体取决于所需的位数,每个可能成员占据一个位。集合直接等同于按位二进制操作。 - Barry Kelly
Cesar:如果它没有出现故障,那么就不要去“修复”它! - Craig Stuntz
@Barry,谢谢,现在我明白了这个是怎么回事,并且学会了如何转换为Byte。 - Cesar Romero

1

这个页面描述了Delphi运算符,包括位运算符。

听起来你需要使用and运算符。例如:

const
  LOADED_FROM_DATABASE = 1;
  PERSISTED = 2;
  CHANGED = 4;
  // etc...

//...

if (bitFlags and LOADED_FROM_DATABASE) <> 0 then
begin
  // handle LOADED FROM DATABASE
end;

if (bitFlags and PERSISTED) <> 0 then
begin
  // handle PERSISTED
end;

// etc...

为了设置标志,您可以使用OR:

bitFlags := LOADED_FROM_DATABASE or PERSISTED or CHANGED;

集合在 Delphi 中是更好的原始类型,因为它们是语言的语法和语义的一部分,但编译成相同的机器码操作。 - Barry Kelly
@Barry,我甚至没有想到那个,太棒了! - Scott W

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接