有没有一种更简单的方法来定义基于布尔值的枚举类型?

6

概述

不确定问题是否表达得恰当,但我之前问过这个问题与此相关:如何正确地将Set实现为类属性?

我喜欢尽可能地保持代码短小、简洁易读,这是我认为某些代码可以更好地编写的地方,但我遇到了一些问题。


首先是两种读取Set值的例子:

冗长的方式:

if (Delphi1 in IDECompatibility) then
  CheckListBox1.Checked[0] := True;
if (Delphi2 in IDECompatibility) then
  CheckListBox1.Checked[1] := True;
if (Delphi3 in IDECompatibility) then
  CheckListBox1.Checked[2] := True;

更简短更好的方法:
CheckListBox1.Checked[0] := (Delphi1 in IDECompatibility);
CheckListBox1.Checked[1] := (Delphi2 in IDECompatibility);
CheckListBox1.Checked[2] := (Delphi3 in IDECompatibility);

现在我想以另一种方式来设置值。

目前我所知道的唯一方法是冗长的:

if CheckListBox1.Checked[0] then
  IDECompatibility := IDECompatibility + [Delphi1]
else
  IDECompatibility := IDECompatibility - [Delphi1];

if CheckListBox1.Checked[1] then
  IDECompatibility := IDECompatibility + [Delphi2]
else
  IDECompatibility := IDECompatibility - [Delphi2];

if CheckListBox1.Checked[2] then
  IDECompatibility := IDECompatibility + [Delphi3]
else
  IDECompatibility := IDECompatibility - [Delphi3];

如果可能的话,我希望能做到像这样:
IDECompatibility[Delphi1] := CheckListBox1.Checked[0]; // Array type required
IDECompatibility[Delphi2] := CheckListBox1.Checked[1]; // Array type required
IDECompatibility[Delphi3] := CheckListBox1.Checked[2]; // Array type required

这里有ExcludeInclude成员,但我不确定它们是否在这里需要。

因此,如上所述 - 是否有更简单的方法根据布尔值定义枚举类型?

谢谢。


你确定在这里使用枚举是正确的选择吗?你现在知道将来会有更多的枚举值,而这些值今天你无法命名(如XE7或XTE...)。对我来说,我会使用一个集合。 - Sir Rufo
3
无论他的用例是什么,如果能够像布尔数组一样遍历枚举集合的语法,那将非常好。 - Stefan Glienke
请翻译以下与编程有关的内容,从英文到中文。仅返回已翻译的文本:忽略 Delphi1Delphi2Delphi3,这只是为了举例说明。 - user1175743
你能用一个通用列表 TList<TSomeEnum> 或者更好的自定义 TList<T> 来实现这个吗?这样可以避免重复等问题。 - J...
你是在寻找一个简单的UI表示集合的方式,而不是OI的另一种方式,对吗? - Sir Rufo
显示剩余13条评论
3个回答

5

我知道你提到了XE版本,但是有些人看到这个问题可能会对更新的版本更感兴趣。

在XE6中,你可以使用一个集合辅助类来实现这个功能:

type
  TMyRange = 0..7;
  TMySet = set of TMyRange;

type
  TMySetHelper = record helper for TMySet
  public
    function GetElement(Index: TMyRange): Boolean;
    procedure SetElement(Index: TMyRange; const Value: Boolean);
    property Element[Index: TMyRange]: Boolean read GetElement write SetElement;
  end;

由于不允许将元素设为默认数组属性,因此您需要指定属性名称。

var
  MySet: TMySet;
begin
  MySet.Element[0] := False;
  MySet.Element[1] := not MySet.Element[0];
end;

这个问题提到的集合是一个属性。记录助手能用于属性吗?我不这样认为,但现在无法验证... - TLama
@TLama,至少它可以编译。 - Uwe Raabe
有趣。这样可能会起作用。这让我想知道如果有一个这样的属性的setter会发生什么。在那种情况下,我会期望编译器失败。 - TLama
@TLama, 我同意它一开始就不应该起作用。同样,在该集合属性上的普通Include或Exclude也不应该起作用。 - Uwe Raabe
Uwe,你可能误解了我的意思。我在抱怨奇怪的语法,它允许将“record helper”类型与“set”类型相关联。现在我站在Free Pascal这一边。 - Free Consulting
显示剩余2条评论

5
目前在Delphi中,set类型没有类似于数组的访问方式。我建议创建一个帮助类索引属性,模仿数组访问方式并隐藏包含或排除元素所需的代码。
从我目前看到的情况来看,人们通常为具有某些基本访问权限的属性添加IsHas前缀。在您的情况下,我会遵循这个规则,并创建一个名为HasIDECompatibility或者说IsIDECompatible的属性。该属性将是Boolean类型,并且具有getter,在其中返回元素是否在内部集合字段中。在setter中,它将根据输入值包含或排除元素到集合中。
在更一般化的代码中,它应该是:
type
  TElement = (
    Element1,
    Element2,
    Element3
  );
  TElements = set of TElement;

  TMyClass = class
  private
    FElements: TElements;
    function GetElement(Kind: TElement): Boolean;
    procedure SetElement(Kind: TElement; Value: Boolean);
  public
    // this property is for direct access to the set field; if your class would
    // be a TComponent descendant and you'd publish this property, you'd see it
    // in the Object Inspector represented by a set of check boxes
    property Elements: TElements read FElements write FElements;
    // this property is just a helper for array like access to the internal set
    // field
    property HasElement[Kind: TElement]: Boolean read GetElement write SetElement;
  end;

implementation

{ TMyClass }

function TMyClass.GetElement(Kind: TElement): Boolean;
begin
  Result := Kind in FElements;
end;

procedure TMyClass.SetElement(Kind: TElement; Value: Boolean);
begin
  if Value then
    Include(FElements, Kind)
  else
    Exclude(FElements, Kind);
end;

还有一个使用示例:

procedure TForm1.Button1Click(Sender: TObject);
var
  MyClass: TMyClass;
begin
  MyClass := TMyClass.Create;
  try
    // write elementary property value
    MyClass.HasElement[Element1] := CheckBox1.Checked;
    MyClass.HasElement[Element2] := CheckBox2.Checked;
    // read elementary property value
    CheckBox3.Checked := MyClass.HasElement[Element1];
    CheckBox4.Checked := MyClass.HasElement[Element2];

    // or you can access MyClass.Elements set at once as you were used to do
    // which gives you two ways to include or exclude elements to the set
  finally
    MyClass.Free;
  end;
end;

这看起来很有前途,而且答案也讲解得很清楚 :) - user1175743
如果存在明确的方法,这个可以作为记录存储在堆栈上,不是吗? - LU RD
我的意思是,可以使用记录(record)而不是用一个类来操作堆(heap)。但在使用记录之前,仍需要使用一个方法清除 FElements 内容。 - LU RD
@LURD,TMyClass是原始问题中的类(来自链接的问题),我展示的只是对其进行的扩展(为了更好地阅读此问题而进行了修改)。实际上,我刚刚添加的是HasElement属性。 - TLama
啊,现在我明白了。我没有跟随那个链接。对不起。 - LU RD

0

基于Uwe的解决方案,我创建了这个帮助程序(需要XE6),它可以用于变量属性:

TGridOptionsHelper = record helper for TGridOptions
public
  ///  <summary>Sets a set element based on a Boolean value</summary>
  ///  <example>
  ///    with MyGrid do Options:= Options.SetOption(goEditing, False);
  ///    MyVariable.SetOption(goEditing, True);
  ///  </example>
  function SetOption(GridOption: TGridOption; const Value: Boolean): TGridOptions;
end;

function TGridOptionsHelper.SetOption(
  GridOption: TGridOption; const Value: Boolean): TGridOptions;
begin
  if Value then Include(Self, GridOption) else Exclude(Self, GridOption);
  Result:= Self;
end;

备注:与Uwe的解决方案相比,我还省略了读取集合元素的代码 - 应该使用in运算符来完成。


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