什么时候:=不作为赋值运算符?

时间:2021-03-10 22:29:40

In the following code := doesn't work as an assignment operator. The help says "The := symbol is sometimes called the assignment operator." I couldn't find in the help when it is not called the assignment operater.

在以下代码中:=不作为赋值运算符。帮助说“The:= symbol有时被称为赋值运算符”。当它没有被称为赋值操作符时,我在帮助中找不到。

procedure TForm1.TestProcedure;
var
  _Integer   , _TemporaryInteger   : Integer;
  _StringList, _TemporaryStringList: TStringList;
begin
  _Integer := 1;

  _TemporaryInteger := _Integer;

  _TemporaryInteger := 2; // doesn't change the original value that _Integer is 1

  _StringList := TStringList.Create;

  _StringList.Add('a');

  _TemporaryStringList := _StringList;

  _TemporaryStringList[0] := 'b'; // changes the original value _StringList[0] into 'b'
end;

2 个解决方案

#1


In Object Pascal := is always an assignment operator.

在Object Pascal中:=始终是赋值运算符。

The documentation wording is perhaps misleading in this respect. It does not mean that this operator sometimes does something else, only that the operator is sometimes referred to as the assignment operator.

在这方面,文件措辞可能会产生误导。这并不意味着此运算符有时会执行其他操作,只是操作符有时被称为赋值运算符。

The distinction is the same as when I say that I sometimes refer to my female parent as 'mother'. She is always my mother but sometimes I call her "mum" or "granny" (when talking to my children) or using her name. But she is always my mother. I just don't always refer to her using that precise term.

区别与我说有时将我的母亲称为“母亲”时的区别相同。她总是我的母亲,但有时我称她为“妈妈”或“奶奶”(与我的孩子交谈时)或使用她的名字。但她永远是我的母亲。我并不总是使用那个精确的术语来指她。

Also be aware that there are some occasions when assignment is achieved using a simple = symbol. For example, identifying default parameter values:

还要注意,在某些情况下,使用简单=符号实现赋值。例如,标识默认参数值:

procedure SomeUsefulProc(const aSwitch: Boolean = FALSE);

Also, when declaring constants or initialising global variables:

此外,在声明常量或初始化全局变量时:

const
  DEFAULT_PREFIX = 'PFX';

var
  gMeaningOfLife: Integer = 42;

However, this does not alter the fact that := is the assignment operator.

但是,这并没有改变以下这一事实:=是赋值运算符。

The assignments in these other cases = are not "operations" in code, but declarations.

这些其他情况下的赋值=不是代码中的“操作”,而是声明。

Properties vs Variables

The confusion in your question arises not from a difference in the meaning of the := symbol, but rather in the fact that your first example involves a variable where-as the second involves a property of an object.

您问题中的混淆不是因为:=符号的含义不同,而是因为您的第一个示例涉及变量,而第二个示例涉及对象的属性。

Objects may declare properties where the assignment of a value to a property actually calls a procedure to apply the new value. The TStringList is a good example.

对象可以声明属性,其中赋值给属性实际上调用应用新值的过程。 TStringList就是一个很好的例子。

After adding an item to a string list, that list has an item that may be accessed using the index of that item in the Strings property:

将项添加到字符串列表后,该列表中有一个项可以使用Strings属性中该项的索引进行访问:

stringList.Add('a');

// stringList.Strings[0] has the value 'a'

The Strings property is declared as the default property of the TStringList class meaning that if an "array" style index is used without any property name, then this Strings property will be assumed by default.

Strings属性被声明为TStringList类的默认属性,这意味着如果使用没有任何属性名称的“数组”样式索引,则默认情况下将使用此Strings属性。

i.e. these two lines of code are exactly equivalent:

即这两行代码完全相同:

s := stringList.Strings[0];
s := stringList[0];

The Strings property is implemented using a function to retrieve the value of specific items (Get) and a procedure to modify them (Put). The above lines of code are equivalent to using the Get function with 0 as the parameter:

Strings属性使用函数来实现,以检索特定项的值(Get)和修改它们的过程(Put)。上面的代码行相当于使用Get函数,其中0为参数:

s := stringList.Get(0);

Since the Strings property is declared with a write accessor then you can also write to this property, thereby calling the Put procedure. To write to a property you assign a value to it, using ... the assignment operator. In this case, the array index of the property is passed to the procedure along with the value to be assigned.

由于Strings属性是使用写访问器声明的,因此您也可以写入此属性,从而调用Put过程。要写入属性,请使用...赋值运算符为其赋值。在这种情况下,属性的数组索引将与要分配的值一起传递给过程。

As before, since the Strings property is the default property for a stringlist, the following two lines of code are exactly equivalent:

和以前一样,由于Strings属性是stringlist的默认属性,因此以下两行代码完全等效:

stringList[0]         := 'b';
stringList.Strings[0] := 'b';

And in both cases the effect is to result in a call to the write procedure for the Strings property:

在这两种情况下,效果是导致对Strings属性的写过程的调用:

stringList.Put(0, 'b');

The operator is still the assignment operator and we still say that we have "assigned a value" to the property. Assignment, in code (as opposed to in a declaration), is always performed using the assignment operator:

运算符仍然是赋值运算符,我们仍然说我们已经为该属性“赋值”。代码中的赋值(与声明中的相反)始终使用赋值运算符执行:

aVariable            := value;
aObject.SomeProperty := value;

The difference is that in the case of an object property the response to that assignment is dependent upon the implementation of the property involved. It may simply modify some value on the object directly, or it may call a procedure to first validate the value being assigned or perform some more complex response to that property change.

不同之处在于,在对象属性的情况下,对该赋值的响应取决于所涉及的属性的实现。它可以直接修改对象上的某个值,或者它可以调用过程来首先验证所分配的值,或者对该属性更改执行一些更复杂的响应。

In the case of a TStringList for example, changing an item in the list not only modifies that item but also triggers a change notification event.

例如,在TStringList的情况下,更改列表中的项目不仅会修改该项目,还会触发更改通知事件。

Records vs Objects (or Values vs References)

In more recent versions of Delphi you may also have properties with read/write functions implemented by record types. These follow the same implementation pattern as properties on objects (i.e. classes). However, in that case you will observe very different results since a record is a value type, not a reference type.

在最新版本的Delphi中,您可能还具有通过记录类型实现的读/写功能的属性。它们遵循与对象(即类)的属性相同的实现模式。但是,在这种情况下,您将观察到非常不同的结果,因为记录是值类型,而不是引用类型。

NOTE: For the examples that follow for simplicity I am using a property that simply directly reads and writes the member rather than using Get/Set functions, but the principle is the same and also applies even if a member of an object is exposed directly (i.e. without an explicit property declaration).

注意:对于简单后面的示例,我使用的属性只是直接读取和写入成员而不是使用Get / Set函数,但原理是相同的,即使直接暴露对象的成员也适用(即没有明确的财产声明)。

If TMyData is implemented as a class then the variables a and b are references to an instance of that class. This must be explicitly created and when we assign one to the other all we are doing is creating a duplicate reference to the same object:

如果将TMyData实现为类,则变量a和b是对该类的实例的引用。这必须是显式创建的,当我们将一个分配给另一个时,我们正在做的是创建对同一个对象的重复引用:

type
  TMyData = class
  private
    fName: String;
  public
    Age: Integer;  // Directly exposed member - effectively a read/write property
    property Name: String read fName write fName;
  end;

var
  a: TMyData;
  b: TMyData;
begin
  a := TMyDate.Create; // Create an object and store a reference in a
  try
    a.Name := 'foo';  // Set the name and age of the object we just created
    a.Age  := 42;

    b := a;           // b now also REFERENCES the same object as a
                      //  a.Name = 'foo' / Age = 42
                      //  b.Name = 'foo' / Age = 42

    b.Name := 'bar';  // We changed the Name of the object to 'bar'.  
                      //  a.Name = 'bar' / Age = 42
                      //  b.Name = 'bar' / Age = 42
                      //  a = b (they reference the same object)
  finally
    a.Free; // both a and b references are now invalid (the object has been destroyed)
end;

If TMyData is instead implemented as a record then a significant difference is introduced. There is no longer any need to explicitly create or destroy objects, and when we assign values of this record type, we create copies of the value involved rather than a reference to it:

如果将TMyData实现为记录,则引入显着差异。不再需要显式创建或销毁对象,当我们分配此记录类型的值时,我们创建所涉及的值的副本而不是对它的引用:

type
  TMyData = record
  private
    fName: String;
  public
    Age: Integer;  // Directly exposed member - effectively a read/write property
    property Name: String read fName write fName;
  end;

In this case if we have two variables of this type then right from the start we have two separate TMyData records and when we assign one to the other, we are copying the entire TMyData value:

在这种情况下,如果我们有两个这种类型的变量,那么从一开始我们有两个单独的TMyData记录,当我们将一个分配给另一个时,我们正在复制整个TMyData值:

var
  a: TMyData;
  b: TMyData;
begin
                    // a and b are both new, initialised records:
                    //   a.Name = '' / Age = 0
                    //   b.Name = '' / Age = 0

  a.Name := 'foo';  // We have set the Name and age of a.  b is unchanged
  a.Age  := 42;     //   a.Name = 'foo' / Age = 42
                    //   b.Name = ''    / Age = 0

  b := a;           // b is now a COPY of a.
                    //   a.Name = 'foo' / Age = 42
                    //   b.Name = 'foo' / Age = 42

  b.Name := 'bar';  // We changed the Name and age of b.  
  b.Age  := 84;     //   a.Name = 'foo' / Age = 42
                    //   b.Name = 'bar' / Age = 84

end;

#2


In this case the variable is managed by value.

在这种情况下,变量由值管理。

_TemporaryInteger := _Integer;
_TemporaryInteger := 2; // doesn't change the original value that _Integer is 1

When a variable is by managed by value, the variable value is copied the new variable and both are in different areas of memory.

当变量由值管理时,变量值将被复制到新变量中,并且两者都位于不同的内存区域中。

In this case the variable is managed by reference (almost all the objects are managed by reference)

在这种情况下,变量由引用管理(几乎所有对象都通过引用管理)

_TemporaryStringList := _StringList;
_TemporaryStringList[0] := 'b'; // changes the original value _StringList[0] into 'b'

When you have a variable managed by reference, the variable reference (pointer) is copied to the new variable and both variable are pointing to the same space of memory (because of that when you change a value in the second variable it changed in the first variables too)

当你有一个通过引用管理的变量时,变量引用(指针)被复制到新变量,并且两个变量都指向相同的内存空间(因为当你更改第二个变量中的值时,它在第一个变量中变量也)

Hope this helps

希望这可以帮助

#1


In Object Pascal := is always an assignment operator.

在Object Pascal中:=始终是赋值运算符。

The documentation wording is perhaps misleading in this respect. It does not mean that this operator sometimes does something else, only that the operator is sometimes referred to as the assignment operator.

在这方面,文件措辞可能会产生误导。这并不意味着此运算符有时会执行其他操作,只是操作符有时被称为赋值运算符。

The distinction is the same as when I say that I sometimes refer to my female parent as 'mother'. She is always my mother but sometimes I call her "mum" or "granny" (when talking to my children) or using her name. But she is always my mother. I just don't always refer to her using that precise term.

区别与我说有时将我的母亲称为“母亲”时的区别相同。她总是我的母亲,但有时我称她为“妈妈”或“奶奶”(与我的孩子交谈时)或使用她的名字。但她永远是我的母亲。我并不总是使用那个精确的术语来指她。

Also be aware that there are some occasions when assignment is achieved using a simple = symbol. For example, identifying default parameter values:

还要注意,在某些情况下,使用简单=符号实现赋值。例如,标识默认参数值:

procedure SomeUsefulProc(const aSwitch: Boolean = FALSE);

Also, when declaring constants or initialising global variables:

此外,在声明常量或初始化全局变量时:

const
  DEFAULT_PREFIX = 'PFX';

var
  gMeaningOfLife: Integer = 42;

However, this does not alter the fact that := is the assignment operator.

但是,这并没有改变以下这一事实:=是赋值运算符。

The assignments in these other cases = are not "operations" in code, but declarations.

这些其他情况下的赋值=不是代码中的“操作”,而是声明。

Properties vs Variables

The confusion in your question arises not from a difference in the meaning of the := symbol, but rather in the fact that your first example involves a variable where-as the second involves a property of an object.

您问题中的混淆不是因为:=符号的含义不同,而是因为您的第一个示例涉及变量,而第二个示例涉及对象的属性。

Objects may declare properties where the assignment of a value to a property actually calls a procedure to apply the new value. The TStringList is a good example.

对象可以声明属性,其中赋值给属性实际上调用应用新值的过程。 TStringList就是一个很好的例子。

After adding an item to a string list, that list has an item that may be accessed using the index of that item in the Strings property:

将项添加到字符串列表后,该列表中有一个项可以使用Strings属性中该项的索引进行访问:

stringList.Add('a');

// stringList.Strings[0] has the value 'a'

The Strings property is declared as the default property of the TStringList class meaning that if an "array" style index is used without any property name, then this Strings property will be assumed by default.

Strings属性被声明为TStringList类的默认属性,这意味着如果使用没有任何属性名称的“数组”样式索引,则默认情况下将使用此Strings属性。

i.e. these two lines of code are exactly equivalent:

即这两行代码完全相同:

s := stringList.Strings[0];
s := stringList[0];

The Strings property is implemented using a function to retrieve the value of specific items (Get) and a procedure to modify them (Put). The above lines of code are equivalent to using the Get function with 0 as the parameter:

Strings属性使用函数来实现,以检索特定项的值(Get)和修改它们的过程(Put)。上面的代码行相当于使用Get函数,其中0为参数:

s := stringList.Get(0);

Since the Strings property is declared with a write accessor then you can also write to this property, thereby calling the Put procedure. To write to a property you assign a value to it, using ... the assignment operator. In this case, the array index of the property is passed to the procedure along with the value to be assigned.

由于Strings属性是使用写访问器声明的,因此您也可以写入此属性,从而调用Put过程。要写入属性,请使用...赋值运算符为其赋值。在这种情况下,属性的数组索引将与要分配的值一起传递给过程。

As before, since the Strings property is the default property for a stringlist, the following two lines of code are exactly equivalent:

和以前一样,由于Strings属性是stringlist的默认属性,因此以下两行代码完全等效:

stringList[0]         := 'b';
stringList.Strings[0] := 'b';

And in both cases the effect is to result in a call to the write procedure for the Strings property:

在这两种情况下,效果是导致对Strings属性的写过程的调用:

stringList.Put(0, 'b');

The operator is still the assignment operator and we still say that we have "assigned a value" to the property. Assignment, in code (as opposed to in a declaration), is always performed using the assignment operator:

运算符仍然是赋值运算符,我们仍然说我们已经为该属性“赋值”。代码中的赋值(与声明中的相反)始终使用赋值运算符执行:

aVariable            := value;
aObject.SomeProperty := value;

The difference is that in the case of an object property the response to that assignment is dependent upon the implementation of the property involved. It may simply modify some value on the object directly, or it may call a procedure to first validate the value being assigned or perform some more complex response to that property change.

不同之处在于,在对象属性的情况下,对该赋值的响应取决于所涉及的属性的实现。它可以直接修改对象上的某个值,或者它可以调用过程来首先验证所分配的值,或者对该属性更改执行一些更复杂的响应。

In the case of a TStringList for example, changing an item in the list not only modifies that item but also triggers a change notification event.

例如,在TStringList的情况下,更改列表中的项目不仅会修改该项目,还会触发更改通知事件。

Records vs Objects (or Values vs References)

In more recent versions of Delphi you may also have properties with read/write functions implemented by record types. These follow the same implementation pattern as properties on objects (i.e. classes). However, in that case you will observe very different results since a record is a value type, not a reference type.

在最新版本的Delphi中,您可能还具有通过记录类型实现的读/写功能的属性。它们遵循与对象(即类)的属性相同的实现模式。但是,在这种情况下,您将观察到非常不同的结果,因为记录是值类型,而不是引用类型。

NOTE: For the examples that follow for simplicity I am using a property that simply directly reads and writes the member rather than using Get/Set functions, but the principle is the same and also applies even if a member of an object is exposed directly (i.e. without an explicit property declaration).

注意:对于简单后面的示例,我使用的属性只是直接读取和写入成员而不是使用Get / Set函数,但原理是相同的,即使直接暴露对象的成员也适用(即没有明确的财产声明)。

If TMyData is implemented as a class then the variables a and b are references to an instance of that class. This must be explicitly created and when we assign one to the other all we are doing is creating a duplicate reference to the same object:

如果将TMyData实现为类,则变量a和b是对该类的实例的引用。这必须是显式创建的,当我们将一个分配给另一个时,我们正在做的是创建对同一个对象的重复引用:

type
  TMyData = class
  private
    fName: String;
  public
    Age: Integer;  // Directly exposed member - effectively a read/write property
    property Name: String read fName write fName;
  end;

var
  a: TMyData;
  b: TMyData;
begin
  a := TMyDate.Create; // Create an object and store a reference in a
  try
    a.Name := 'foo';  // Set the name and age of the object we just created
    a.Age  := 42;

    b := a;           // b now also REFERENCES the same object as a
                      //  a.Name = 'foo' / Age = 42
                      //  b.Name = 'foo' / Age = 42

    b.Name := 'bar';  // We changed the Name of the object to 'bar'.  
                      //  a.Name = 'bar' / Age = 42
                      //  b.Name = 'bar' / Age = 42
                      //  a = b (they reference the same object)
  finally
    a.Free; // both a and b references are now invalid (the object has been destroyed)
end;

If TMyData is instead implemented as a record then a significant difference is introduced. There is no longer any need to explicitly create or destroy objects, and when we assign values of this record type, we create copies of the value involved rather than a reference to it:

如果将TMyData实现为记录,则引入显着差异。不再需要显式创建或销毁对象,当我们分配此记录类型的值时,我们创建所涉及的值的副本而不是对它的引用:

type
  TMyData = record
  private
    fName: String;
  public
    Age: Integer;  // Directly exposed member - effectively a read/write property
    property Name: String read fName write fName;
  end;

In this case if we have two variables of this type then right from the start we have two separate TMyData records and when we assign one to the other, we are copying the entire TMyData value:

在这种情况下,如果我们有两个这种类型的变量,那么从一开始我们有两个单独的TMyData记录,当我们将一个分配给另一个时,我们正在复制整个TMyData值:

var
  a: TMyData;
  b: TMyData;
begin
                    // a and b are both new, initialised records:
                    //   a.Name = '' / Age = 0
                    //   b.Name = '' / Age = 0

  a.Name := 'foo';  // We have set the Name and age of a.  b is unchanged
  a.Age  := 42;     //   a.Name = 'foo' / Age = 42
                    //   b.Name = ''    / Age = 0

  b := a;           // b is now a COPY of a.
                    //   a.Name = 'foo' / Age = 42
                    //   b.Name = 'foo' / Age = 42

  b.Name := 'bar';  // We changed the Name and age of b.  
  b.Age  := 84;     //   a.Name = 'foo' / Age = 42
                    //   b.Name = 'bar' / Age = 84

end;

#2


In this case the variable is managed by value.

在这种情况下,变量由值管理。

_TemporaryInteger := _Integer;
_TemporaryInteger := 2; // doesn't change the original value that _Integer is 1

When a variable is by managed by value, the variable value is copied the new variable and both are in different areas of memory.

当变量由值管理时,变量值将被复制到新变量中,并且两者都位于不同的内存区域中。

In this case the variable is managed by reference (almost all the objects are managed by reference)

在这种情况下,变量由引用管理(几乎所有对象都通过引用管理)

_TemporaryStringList := _StringList;
_TemporaryStringList[0] := 'b'; // changes the original value _StringList[0] into 'b'

When you have a variable managed by reference, the variable reference (pointer) is copied to the new variable and both variable are pointing to the same space of memory (because of that when you change a value in the second variable it changed in the first variables too)

当你有一个通过引用管理的变量时,变量引用(指针)被复制到新变量,并且两个变量都指向相同的内存空间(因为当你更改第二个变量中的值时,它在第一个变量中变量也)

Hope this helps

希望这可以帮助