Получение возможных и приведенных значений из типа tkSet с использованием RTTI

#delphi #rtti

#delphi #rtti

Вопрос:

Возможно, кто-то уже задавал этот вопрос, но не нашел его, так что проблема решена:

Я хочу проанализировать свойства tkSet компонента (Panel1 в нашем случае), но я не знаю, как это сделать правильно. Я смог найти базовый перечисляемый тип набора, используя rContext.FindType() , но я почти уверен, что есть какой-то более элегантный / простой способ сделать это. С этого момента я полностью потерян. Я должен пройти через значения этого типа перечисления и проверить каждое значение на соответствие текущему значению свойства компонента.

 procedure TForm12.GetProperties2;
var
  rContext: TRttiContext;
  rType: TRttiType;
  rProp: TRttiProperty;
begin
  rType := rContext.GetType(Panel1.ClassType);

  for rProp in rType.GetProperties do
  begin
    if (rProp.Visibility in [mvPublished]) and (rProp.PropertyType.TypeKind in [tkSet]) and (rProp.Name = 'Anchors') then
    begin
      Memo1.Lines.Add('Name: '   rProp.Name);
      Memo1.Lines.Add('PropertyType: '   rProp.PropertyType.ToString);
      Memo1.Lines.Add('Value: '   rProp.GetValue(Panel1).ToString);
      Memo1.Lines.Add('QualifiedName: '   rProp.PropertyType.QualifiedName);
      Memo1.Lines.Add('ElementType: '   rContext.FindType(rProp.PropertyType.QualifiedName).AsSet.ElementType.ToString);
      // here comes the desired results
      Memo1.Lines.Add('Possible values:');
      Memo1.Lines.Add(' 0 > akLeft');
      Memo1.Lines.Add(' 1 > akTop');
      Memo1.Lines.Add(' 2 > akRight');
      Memo1.Lines.Add(' 3 > akBottom');
      Memo1.Lines.Add('Present values:');
      Memo1.Lines.Add(' 0 > akLeft');
      Memo1.Lines.Add(' 1 > akTop');
      Memo1.Lines.Add('');
    end;
  end;
end;
  

Другой возможной проблемой является набор свойств без базовых перечисляемых типов, например, если вы посмотрите на TPanel.Свойство StyleElements вы можете видеть, что объявление TStyleElements выглядит следующим образом:

 TStyleElements = set of (seFont, seClient, seBorder);
  

В этом случае тип элемента не работает.

Итак, вопрос в том, как я могу проанализировать свойства типа tkSet, чтобы получить желаемые результаты, используя контекст RTTI?

Комментарии:

1. Почему это должно быть без модуля TypInfo?

2. потому что я хочу сейчас изучить функции RTTI

3. Это расширенный RTTI. Я не думаю, что новый RTTI является полной заменой. Он использует по крайней мере PTypeInfo, который все еще находится в unit TypInfo.

4. Я понимаю. Значит, для решения этой проблемы должен быть задействован TypInfo?

5. @tcxbalage: Маловероятно, что вы сможете добиться чего-либо полезного с помощью RTTI без модуля TypInfo.

Ответ №1:

Это довольно просто с помощью базового TypInfo.

 procedure PrintSet(const v: TValue); // v contains a value from a set type
var
  enumType: PTypeInfo;
  enumData: PTypeData;
  buffer: set of Byte; // biggest possible set type
  i: Integer;
begin
  buffer := [];
  v.ExtractRawData(@buffer);
  enumType := v.TypeInfo.TypeData.CompType^;
  enumData := enumType.TypeData;
  for i := enumData.MinValue to enumData.MaxValue do
    Writeln(GetEnumName(enumType, i)   ' = '   (i in buffer).ToString(TUseBoolStrs.True));
end;
  

A set of Byte — это самый большой возможный тип набора, поэтому мы можем использовать его как буфер, в который все поместится, а затем использовать TValue.ExtractRawData метод для записи в него столько данных, сколько имеет фактический тип набора. Все остальное было обнулено, установив для него значение empty ранее.

Затем мы можем использовать данные типа типа enum для получения минимальных и максимальных значений. Поскольку несмежные перечисляемые типы не имеют typeinfo, нам не нужно заботиться об этом и фактически обрабатывать только те, которые совместимы с двоичными классическими битовыми масками.