【问题标题】:Is there a easier way to define a Enum type based on a boolean value?有没有更简单的方法来定义基于布尔值的 Enum 类型?
【发布时间】:2014-07-05 09:37:59
【问题描述】:

概述

不完全确定这个问题的措辞是否恰当,但我之前问过这个与这个问题相关的问题:How do I correctly implement a Set in a class as a property?

我喜欢让代码尽可能简短、简洁和易读,这就是我认为有些代码可以写得更好但我遇到了问题的地方。


读取 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 成员,但我不确定这里是否需要这些。

那么,如上所述 - 有没有更简单的方法来定义基于布尔值的 Enum 类型?

谢谢。

【问题讨论】:

  • 你确定枚举是正确的选择吗?今天,您现在知道将来会有更多您今天无法命名的枚举值(XE7 或 XTE ...)。对我来说,我会使用一个集合
  • Delphi1Delphi2Delphi3 分别代表什么?您是否尝试根据他们正在运行的 Delphi 版本向最终用户提供兼容性定制?如果是的话,这听起来像是条件编译的情况……
  • 不管他的用例如何,如果一个枚举集语法可以像一个遍历枚举的布尔数组一样使用会很好。
  • 忽略 Delphi1, Delphi2 Delphi3 所显示的内容,这仅用于示例目的。
  • 你能用一个通用列表来做这个吗? TList<TSomeEnum> - 或者更好的自定义 TList<T>;可以防止重复等的东西。

标签: delphi delphi-xe


【解决方案1】:

不,目前在 Delphi 中没有可用于set 类型的数组访问。我建议创建一个辅助索引属性,它可以模仿数组访问,并且将隐藏包含或排除集合中元素所需的代码。

据我目前所见,人们通常使用IsHas 前缀为具有一些基本访问权限的属性添加前缀。在您的情况下,我将遵循此规则并创建一个名为HasIDECompatibility 的属性,或者假设为IsIDECompatible。该属性将是Boolean 类型,并且将有一个getter,通过它您可以返回元素是否在内部set 字段内的信息。在 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;

【讨论】:

  • 这看起来很有希望,也是一个很好解释的答案:)
  • 如果存在 clear 方法,这可以作为记录存在于堆栈中,不是吗?
  • 我的意思是,可以使用记录而不是使用类来访问堆。但是在使用记录之前,您仍然需要通过方法清除 FElements 的内容。
  • @LURD,TMyClass 是最初的 OP 类(来自链接的问题),我所展示的只是它的扩展(好吧,为了更好的可读性,为此目的进行了修改问题)。实际上,我刚刚添加的是HasElement 属性。
  • 啊,现在我明白了。我没有点击那个链接。对不起。
【解决方案2】:

我知道您提到了 XE,但看到这个问题的人可能会对这个答案感兴趣,以获得更新的版本。

在 XE6 中,这可以(几乎)使用 set helper 来完成:

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;

由于不允许将 Element 设为默认数组属性,因此必须指定属性名称。

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

【讨论】:

  • 这个问题中提到的集合是一个属性。记录助手会为财产工作吗?我不这么认为,但目前无法验证...
  • @TLama,至少它可以编译。
  • 有趣。所以它可能会起作用。这让我想知道如果有这样的属性的设置器会发生什么。在这种情况下,我希望编译器会失败。
  • @TLama,我同意它首先不应该工作。与该 set 属性上的普通 Include 或 Exclude 相同也不应该工作。
  • @FreeConsulting,一如既往地是关于“可以完成”和“应该完成”的问题。我自己很欣赏它可以完成,因为在某些情况下它可能会派上用场。不过,我建议尽量少用。
【解决方案3】:

基于 Uwes 解决方案,我创建了这个帮助程序(需要 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;

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

【讨论】:

    猜你喜欢
    • 2012-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-07
    • 1970-01-01
    • 2011-10-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多