Delphi 'in' перегрузка оператора на множестве

В Delphi XE2 я пытаюсь перегрузить оператор in в записи, чтобы я мог проверить, является ли значение, представленное записью, частью набора. Мой код выглядит следующим образом:

type
  MyEnum = (value1, value2, value3);
  MySet = set of MyEnum;
  MyRecord = record
    Value: MyEnum;
    class operator In(const A: MyRecord; B: MySet): Boolean;
  end;

class operator MyRecord.In(const A: MyRecord; B: MySet): Boolean;
begin
  Result := A.Value in B;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  R: MyRecord;
  S: MySet;
begin
  R.Value := value1;
  S := [value1, value2];
  Button1.Caption := BoolToStr(R in S);
end;

Код не удается скомпилировать. Для оператора R in S компилятор говорит: Несовместимые типы MyRecord и MyEnum.

Как я могу перегрузить оператор in на MyRecord, чтобы R in S оценил значение True в приведенном выше коде?

Ответ 1

Хорошо, вы можете это сделать почти так, но можете не хотеть. AFAIK, операторы класса работают только над классом (или записью), который они определены внутри, поэтому оба R и S в вашем коде должны быть TMyRecord. При некотором оскорбительном использовании неявного литья мы получаем следующее:

unit Unit2;
interface
type
  MyEnum = (value1, value2, value3);
  MySet = set of MyEnum;
  MyRecord = record
    Value: MyEnum;
    ValueSet: MySet;
    class operator Implicit(A: MyEnum): MyRecord;
    class operator Implicit(A: MySet): MyRecord;
    class operator In (Left,Right:MyRecord): Boolean;
  end;

implementation

class operator MyRecord.Implicit(A: MyEnum): MyRecord;
begin
  Result.Value := A;
end;

class operator MyRecord.Implicit(A: MySet): MyRecord;
begin
  Result.ValueSet := A;
end;

class operator MyRecord.In(Left, Right: MyRecord): Boolean;
begin
  Result:= left.Value in Right.ValueSet;
end;
end.

Теперь будут выполняться следующие процедуры и даже работать:

procedure TForm1.Button1Click(Sender: TObject);
var
  R: MyRecord;
  S: MyRecord;
begin
  R.Value := value1;
  S := [value1,value2,value3];
  Button1.Caption := BoolToStr(R In S,true);
end;

Что, я уверен, мы все согласны, гораздо более элегантно, чем "BoolToStr (R.Value in S)". Однако следующее будет компилироваться, но дать неправильный результат:

procedure TForm1.Button1Click(Sender: TObject);
var
  R: MyRecord;
  S: MyRecord;
begin
  R.Value := value1;
  S := [value1,value2,value3];
  Button1.Caption := BoolToStr(S In R,true);
end;

Итак, как прокомментировал Дорин, лучше просто иметь скучный, старомодный старый "BoolToStr" (R.Value in S) ". Если, конечно, вам не платят за строку кода. И бонус для исправления ошибок.

Ответ 2

Для того чтобы оператор in работал, правый операнд должен быть типа записи, поскольку он является оператором set, а не бинарным оператором. В вашем случае это левый операнд.

Итак, будет работать следующее:

type
  MyRecord = record
    Value: MyEnum;
    class operator In(const A: MyRecord; const B: MySet): Boolean;
  end;

  MyRecord2 = record
    Value: MySet;
    class operator In(const A: MyRecord; const B: MyRecord2): Boolean;
    class operator In(const A: MyEnum; const B: MyRecord2): Boolean;
  end;

class operator MyRecord.In(const A: MyRecord; const B: MySet): Boolean;
begin
  Result := A.Value in B;
end;

class operator MyRecord2.In(const A: MyRecord; const B: MyRecord2): Boolean;
begin
  Result := A.Value in B.Value;
end;

class operator MyRecord2.In(const A: MyEnum; const B: MyRecord2): Boolean;
begin
  Result := A in B.Value;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  R: MyRecord;
  R2: MyRecord2;
begin
  R.Value := value1;
  R2.Value := [value1, value2];

  if R in R2 then;
  if value1 in R2 then;
end;