标记/取消标记一组枚举中的另一个选项

Mark / Unmarked another option from a set of enum

我在 component 中有一组 enum 像这样:

type
  TOption = (clVisible, clVisibleAlways, clRenderable, clEditable);
  TOptions = set of TOption;

const 
  defaultOptions = [clVisible, clRenderable];

type
  TMyComp = class(TComponent)
  private
    FOptions: TOptions;
    procedure SetOptions(const Value: TOptions);
  public
    property Options: TOptions read FOptions write SetOptions default defaultOptions;

...

procedure TMyComp.SetOptions(const Value: TOptions);
var
  ToBeSet, ToBeCleared: TOptions;
begin
  if FOptions <> Value then
    begin
      ToBeCleared:= FOptions - Value;
      ToBeSet:= Value - FOptions;

      FOptions:= Value;

      //clVisible -> clRenderable
      if (clVisible in ToBeSet) and (clRenderable in ToBeCleared)  then
        begin
          Include(FOptions, clRenderable);
          Include(ToBeSet, clRenderable);
        end;

      //not clRenderable -> not clVisible
      if (clRenderable in ToBeCleared) and (clVisible in ToBeSet)  then
        begin
          Exclude(FOptions, clVisible);
          Exclude(ToBeSet, clVisible);
        end;

      //not clVisible -> not clVisibleAlways
      if (clVisible in ToBeCleared) and (clVisibleAlways in ToBeSet) then
        begin
          Exclude(FOptions, clVisibleAlways);
          Exclude(ToBeSet, clVisibleAlways);
        end;

      //clVisibleAlways -> clVisible
      if (clVisibleAlways in ToBeSet) and (clVisible in ToBeCleared) then
        begin
          Include(FOptions, clVisible);
          Include(ToBeSet, clVisible);
        end;
  end;
end;

我想做但行不通的是:

请大家支持一下这个话题。

您可以将 TOption 集包装到助手 class 中,这将提供额外的赋值逻辑:

TOption = (clVisible, clVisibleAlways, clRenderable, clEditable);
TOptions = set of TOption;

TOptionHelper = class
public
    constructor Create();
    procedure Include(const AOption : TOption);
    procedure Exclude(const AOption : TOption);
    function GetOptions() : TOptions;
    property Options : TOptions read GetOptions;
strict private
    FOptions : TOptions;
end;

constructor TOptionHelper.Create;
begin
    FOptions := [clVisible, clRenderable];
end;

procedure TOptionHelper.Exclude(const AOption: TOption);
begin

end;

function TOptionHelper.GetOptions: TOptions;
begin
    Result := FOptions;
end;

procedure TOptionHelper.Include(const AOption: TOption);
begin
    case AOption of
        clVisibleAlways :  FOptions := FOptions + [clVisible];
        //and so on...
    end;
end;

我认为您的方法不必要地复杂。

更新

必须清楚地区分添加或删除选项(我以前没有这样做)。更新后的代码可以做到这一点,并且在 Delphi 10.1 Berlin 中运行良好。我的测试组件称为 TNewButton 并且基于 TButton.

procedure TNewButton.SetOptions(const Value: TOptions);
var
  Opts: TOptions; { Because of "const"; you can also remove "const" 
                    and use Value instead of Opts. }
begin
  if FOptions <> Value then
  begin
    Opts := Value;
    { Find out if we are adding or removing an option. }
    if Opts - FOptions <> [] then
    begin
      { We are adding an option. }
      if clVisibleAlways in Opts then
        Include(Opts, clVisible);
      if clVisible in Opts then
        Include(Opts, clRenderable)
    end
    else
    begin
      { We are removing an option. }
      if not (clRenderable in Opts) then
        Exclude(Opts, clVisible);
      if not (clVisible in Opts) then
        Exclude(Opts, clVisibleAlways);
    end;
    FOptions := Opts;
  end;
end;

我测试了几次,至少在我的 Object Inspector 中,它完全符合您的要求。

你的逻辑有问题。

线条

  if (clVisible in ToBeSet) and (clRenderable in ToBeCleared)  then

  if (clRenderable in ToBeCleared) and (clVisible in ToBeSet)  then

是等效的 - 但这显然不是您想要的。

我认为 Rudy 的解决方案也不完全正确,因为 set 和 clear 之间存在潜在的矛盾。

这是我建议的解决方案。

procedure TMyComp.SetOptions(const Value: TOptions);
var
  ToBeSet, ToBeCleared: TOptions;
begin
  if FOptions <> Value then
    begin
      ToBeCleared:= FOptions - Value;
      ToBeSet:= Value - FOptions;

      FOptions:= Value;

      //clVisible -> clRenderable
      if (clVisible in ToBeSet)  then
        begin
          Include(FOptions, clRenderable);
          Exclude( ToBeCleared, clRenderable); // avoid contradiction!
        end;

      //not clRenderable -> not clVisible
      if (clRenderable in ToBeCleared)  then
        begin
          Exclude(FOptions, clVisible);
          Exclude(ToBeSet, clVisible);
          Include(ToBeCleared, clVisible ); // Chain reaction - Clearing clRederable clears clVisible and therefore by implication clVisibleAlways!
        end;

      //not clVisible -> not clVisibleAlways
      if (clVisible in ToBeCleared) then
        begin
          Exclude(FOptions, clVisibleAlways);
          Exclude(ToBeSet, clVisibleAlways);
        end;

      //clVisibleAlways -> clVisible
      if (clVisibleAlways in ToBeSet) then
        begin
          Include(FOptions, clVisible);
          Include(ToBeSet, clVisible);
        end;
  end;
end;

我会使用更像这样的东西:

procedure TMyComp.SetOptions(const Value: TOptions);
var
  ToBeSet, ToBeCleared, LNewOptions: TOptions;
begin
  ToBeCleared := FOptions - Value;
  ToBeSet := Value - FOptions;
  LNewOptions := FOptions - ToBeCleared + ToBeSet;

  if (clVisibleAlways in LNewOptions) then
    Include(LNewOptions, clVisible);

  if (clVisible in LNewOptions) then
    Include(LNewOptions, clRenderable);

  if not (clRenderable in LNewOptions) then
    Exclude(LNewOptions, clVisible);

  if not (clVisible in LNewOptions) then
    Exclude(LNewOptions, clVisibleAlways);

  if FOptions <> LNewOptions then
  begin
    FOptions := LNewOptions;
    // update the rest of your component as needed...
  end;
end;

我将添加用于解决问题的完整代码。这将消除一些关于我选中或取消选中选项的顺序的限制。

procedure TMyComp.SetOptions(Value: TOptions);
var
  ToBeSet, ToBeCleared: TOptions;
  clRenderableChanged, clVisibleChanged, clVisibleAlwaysChanged: Boolean;
begin
  if FOptions <> Value then
    begin
      ToBeCleared:= FOptions - Value;
      ToBeSet:= Value - FOptions;

      clRenderableChanged:= (clRenderable in ToBeSet) and (not (clRenderable in FOptions)) or ((clRenderable in ToBeCleared) and (clRenderable in FOptions));
      clVisibleChanged:= (clVisible in ToBeSet) and (not (clVisible in FOptions)) or ((clVisible in ToBeCleared) and (clVisible in FOptions));
      clVisibleAlwaysChanged:= (clVisibleAlways in ToBeSet) and (not (clVisibleAlways in FOptions)) or ((clVisibleAlways in ToBeCleared) and (clVisibleAlways in FOptions));

      FOptions:= Value;

      if clRenderableChanged then
        begin
          if clRenderable in ToBeSet then Include(FOptions, clVisible);
          if clRenderable in ToBeCleared then
            begin
              Exclude(FOptions, clVisible);
              Exclude(FOptions, clVisibleAlways);
            end;
        end;

      if clVisibleChanged then
        begin
          if clVisible in ToBeSet then Include(FOptions, clRenderable);
          if clVisible in ToBeCleared then Exclude(FOptions, clVisibleAlways);
        end;

      if clVisibleAlwaysChanged then
        begin
          if clVisibleAlways in ToBeSet then
            begin
              Include(FOptions, clVisible);
              Include(FOptions, clRenderable);
            end;
        end;
    end;
end;

无论如何,这要归功于所有回答这个问题的人的帮助。

更新 我认为我的一些问题是因为我在安装组件更改后没有重新启动 Delphi。