Перегрузка оператора 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 в приведенном выше коде?


person Jan Goyvaerts    schedule 08.11.2011    source источник
comment
Я не думаю, что то, чего вы пытаетесь достичь, возможно... вам лучше написать дополнительные символы .Value => BoolToStr(R.Value in S ); и покончим с этим   -  person    schedule 08.11.2011
comment
Код в моем вопросе - это просто упрощенный образец. В моем реальном приложении тип записи не имеет однозначного соответствия с типом набора. Обходной путь, который я использовал, состоял в том, чтобы добавить function InSet(S: MySet): Boolean к записи и использовать его вместо оператора in.   -  person Jan Goyvaerts    schedule 01.12.2011
comment
может быть, вместо этого было бы достаточно сделать функцию-член - BoolToStr(R._in(S));   -  person Arioch 'The    schedule 22.03.2016


Ответы (2)


Что ж, вы можете почти сделать это, но, возможно, не захотите. Насколько мне известно, операторы класса работают только с классом (или записью), в котором они определены, поэтому и 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)». Если, конечно, вам не платят за строку кода. И бонус за исправление ошибок.

person HMcG    schedule 09.11.2011
comment
Этот конкретный вопрос был просто упражнением в выяснении операторов класса, которые я никогда раньше не использовал. Тип записи в реальном коде, над которым я работал, намного сложнее. Определяемые им операторы значительно упрощают код, использующий эту запись. Несколько сотен строк простых операторных функций делают тысячи строк сложного кода более читабельными. - person Jan Goyvaerts; 01.12.2011
comment
Извините, мои комментарии были несколько шутливыми. Признаюсь, мне очень понравился новый оператор загрузки записей, и я широко его использую. И я согласен, что во многих случаях это делает код более понятным. Я только высмеивал свое собственное решение, так как в данном конкретном случае это излишне ;-) - person HMcG; 02.12.2011
comment
поэтому и R, и S в вашем коде должны быть TMyRecord, это неверно. - person Johan; 29.01.2014

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

Таким образом, будет работать следующее:

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;
person Stefan Glienke    schedule 08.11.2011