Pascal - 如何使用字符串变量引用类型的字段?

时间:2018-04-08 07:36:59

标签: pascal freepascal

例如我有一个类型:

type_test =record
    x : Integer;
    y : Integer;
    z : Integer;
end;

var
    test_var : type_test;
    test_str : string;

如何使用test_str分配test_var.x,y和z的值?如果像这样的东西能够起作用那就太棒了:

test_str := x;
test_var.test_str := 99;
writeln(test_var.x); //outputs 99

然而,这不起作用。有没有可能的方法来实现这一目标?我需要在数据类型中分配无数的字段,如果我可以使用这种方法使用带有字段名称数组的循环,那就太棒了。

2 个答案:

答案 0 :(得分:1)

您看起来想要和需要的只是在记录类型上声明的字符串属性,它既可以将记录的所有成员数据表示为字符串,也可以允许您设置它,可能是根据某些格式规则。我期待以下内容,例如:

test_var.AsString := '99';         //  Sets x = 99
test_var.AsString := '99,42';      //  Sets x = 99 and y = 42
test_var.AsString := '99,42,57';   //  Sets x = 99, y = 42 and z = 57

一种方法只涉及在记录类型上声明的属性,该属性在使用该属性读取或写入该记录类型的变量时执行和解释所需的格式,在本例中为单个字符串值:

type_test = record
private
  function get_AsString: String;
  procedure set_AsString(const aValue: String);
public
  x : Integer;
  y : Integer;
  z : Integer;
  property AsString: String read get_AsString write set_AsString;
end;


function type_test.get_AsString: String;
begin
  // Return x, y and z formatted as a single string value
end;


procedure type_test.set_AsString(const aValue: String);
begin
  // Unpacks values in the supplied formatted string and 
  //  applies them to x, y, and as appropriate
end;

这些方法的实现细节留给您练习(除了其他任何内容之外,未提供作为String值的记录类型表示的精确规范)。

RTTI的应用可能允许您的字符串格式更通用/更灵活:

test_var.AsString := 'z=99';         //  Sets z = 99

您仍然可以在上面基于AsString属性的方法中支持这一点,只需在您的读/写方法中包含特定的字段命名。 RTTI仅对完全灵活性是必需的,即允许您的读/写方法“发现”受支持的成员字段,而不是明确编码以支持特定的命名成员。

用于读取/写入特定记录类型的值的任何方法都不一定需要是记录类型本身的一部分(尽管这反过来使得这些方法不易被记录类型的消费者发现)。

对类型的显式AsString支持的参数强于读/写记录成员到/来自字符串的通用方法。显式支持涉及实现细节的知识,将AsString方法与其支持的类型紧密耦合。在定义上,按照名称读取/写入记录成员的完全通用方法理论上可以应用于任何记录类型,因此使它们成为任何特定的 记录类型没什么意义。

由于在运行时使用RTTI进行发现所涉及的额外工作,基于RTTI的方法的效率也会稍低。

简而言之,RTTI以复杂性和一些开销为代价提供了更大的灵活性。

这种复杂性和开销是否是公平交换的灵活性取决于你。

答案 1 :(得分:0)

简短的回答是Pascal语言(无论是Delphi还是FreePascal变体)根本不支持您正在寻找的语法。您不能使用在运行时填充的字符串替换成员名称。

但是,您可以使用包含property高级记录来帮助您获得类似于您想要的内容,例如:

{$ModeSwitch AdvancedRecords}

type
  type_test = record
  private
    function GetFieldPtr(const FieldName: String): PInteger;
    function GetFieldValue(const FieldName: String): Integer;
    procedure SetFieldValue(const FieldName: String; AValue: Integer);
  public
    x : Integer;
    y : Integer;
    z : Integer;
    property FieldValue[const FieldName: string]: Integer read GetFieldValue write SetFieldValue default;
  end;

function type_test.GetFieldPtr(const FieldName: String): PInteger;
begin
  // TODO: alternatively, you can use RTTI here to find a field by name...
  //
  // See "FPC : RTTI on records"
  // https://stackoverflow.com/questions/27803383/
  //
  if FieldName = 'x' then
    Result := @x
  else if FieldName = 'y' then
    Result := @y
  else if FieldName = 'z' then
    Result := @z
  else begin
    Result := nil;
    raise Exception.CreateFmt('Unknown Field: "%s"', [FieldName]);
  end;
end;

function type_test.GetFieldValue(const FieldName: String): Integer;
var
  FieldPtr: PInteger;
begin
  FieldPtr := GetFieldPtr(FieldName);
  Result := FieldPtr^;
end;

procedure type_test.SetFieldValue(const FieldName: String; AValue: Integer);
var
  FieldPtr: PInteger;
begin
  FieldPtr := GetFieldPtr(FieldName);
  FieldPtr^ := AValue;
end;

现在你可以这样做:

var
  test_var : type_test;
  test_str : string;
begin
  test_str := 'x';
  test_var[test_str] := 99;
  WriteLn(test_var.x); //outputs 99
end;

如果您的记录包含不同数据类型的字段,事情会变得有点棘手。在这种情况下,您的媒体资源必须使用VariantTValue