在计算机科学中， **变异子方法**（mutator或setter）是一种用于控制变量更改的方法。通常，**变异子**伴随着**访问子** （accessor或getter）返回私有成员变量的值。

变异子方法最常用于面向对象的编程封装，与封装原则保持一致。根据这个原则，类的成员变量被设为私有以隐藏和保护它们不受其他代码的影响，并且只能由公共成员函数（变异子方法）修改，该函数将所需的新值作为参数來修改私有成员变量。可以将变异子方法与赋值运算符重载进行比较。

变异子方法也可以在非面向对象的环境中使用。在这种情况下，对要修改的变量的引用与新值一起传递给变异子。编译器无法限制代码绕过变异子方法并直接更改变量。开发人员有责任确保仅通过变异子方法修改变量，而不是直接修改变量。

在支持它们的编程语言中，属性提供了一种方便的替代方案，而不放弃封装的效用。

在下面的示例中，完全实现的 变异子方法还可以验证输入数据或采取进一步的操作，例如触发事件 。

影响
--

定义变异子和 访问子方法或属性块的另一种方法是为实例变量提供一些可见性，而不是私有化它或直接从对象外部访问它。可以使用修改器和访问器定义更精细的控制访问权限。例如，将一个参数设为只读，可以通过定义一个访问器而不提供修改器。两种方法的可见性可能不同：访问器通常是公开的；变异子是保护的、包私有的或内部的。

定义了变异子的块有机会验证或预处理传入的数据。如果保证所有外部访问都通过变异子，则无法绕过这些步骤。例如，如果日期由单独的私有 `year` 、 `month`和`day`变量表示，则传入日期由变异子`setDate` 拆分，同时为了一致性，相同的私有实例变量由`setYear`和`setMonth`访问。在所有情况下，1 - 12 之外的月份值都可以被相同的代码拒绝。

相反，访问器允许从内部变量合成有用的数据表示，同时保持它们的结构封装和对外部模块不可见。货币`getAmount`访问子可以由一个内部私有的数值变量构建一个字符串，其小数位数由参数`currency`定义。

现代编程语言常提供在一行中修改器和访问器的生成样板—例如 C# 的 `public string Name { get; set; }`和Ruby的 `attr_accessor :name` 。在这些情况下，不会为验证、预处理或合成等操作创建代码块。这些简化的访问器仍然保留了对简单公共实例变量进行封装的优势，但常见的是，随着敏捷软件开发、軟體維護和需求的变化，对数据的需求变得更加复杂。许多自动修改器和访问器最终会被单独的代码块取代。在实现的早期自动创建它们的好处是，无论是否添加了更复杂的类，类的公共接口都保持相同，如果添加，则不需要进行大量重构。 

由于涉及额外的步骤，访问器函数的效率可能低于直接获取或存储数据字段， 然而，这些函数通常是内联的，从而消除了函数调用的开销。

例子
--

### 汇编语言

```
student struct
 age dd ?
student ends

```

```
 .code
student\_get\_age proc object:DWORD
 mov ebx, object
 mov eax, student.age[ebx]
 ret
student\_get\_age endp

student\_set\_age proc object:DWORD, age:DWORD
 mov ebx, object
 mov eax, age
 mov student.age[ebx], eax
 ret
student\_set\_age endp

```

### C

文件student.h:

```
#ifndef \_STUDENT\_H
#define \_STUDENT\_H

struct student; /\* opaque structure \*/
typedef struct student student;

student \*student\_new(int age, char \*name);
void student\_delete(student \*s);

void student\_set\_age(student \*s, int age);
int student\_get\_age(student \*s);
char \*student\_get\_name(student \*s);

#endif

```

文件student.c:

```
#include <stdlib.h>
#include <string.h>
#include "student.h"

struct student {
 int age;
 char \*name;
};

student \*student\_new(int age, char \*name) {
 student \*s = malloc(sizeof(student));
 s->name = strdup(name);
 s->age = age;
 return s;
}

void student\_delete(student \*s) {
 free(s->name);
 free(s);
}

void student\_set\_age(student \*s, int age) {
 s->age = age;
}

int student\_get\_age(student \*s) {
 return s->age;
}

char \*student\_get\_name(student \*s) {
 return s->name;
}

```

文件 main.c:

```
#include <stdio.h>
#include "student.h"

int main(void) {
 student \*s = student\_new(19, "Maurice");
 char \*name = student\_get\_name(s);
 int old\_age = student\_get\_age(s);
 printf("%s's old age = %i\n", name, old\_age);
 student\_set\_age(s, 21);
 int new\_age = student\_get\_age(s);
 printf("%s's new age = %i\n", name, new\_age);
 student\_delete(s);
 return 0;
}

```

文件 Makefile:

```
all: out.txt; cat $<
out.txt: main; ./$< > $@
main: main.o student.o
main.o student.o: student.h
clean: ;$(RM) *.o out.txt main

```

### C++

文件Student.h:

```
#ifndef STUDENT\_H
#define STUDENT\_H

#include <string>

class Student {
public:
 Student(const std::string& name);

 const std::string& name() const;
 void name(const std::string& name);

private:
 std::string name\_;
};

#endif

```

文件Student.cpp:

```
#include "Student.h"

Student::Student(const std::string& name) : name\_(name) {
}

const std::string& Student::name() const {
 return name\_;
}

void Student::name(const std::string& name) {
 name\_ = name;
}

```

### C#

C#支持属性编程。

```
public class Student {
 private string name;

 /// <summary>
 /// Gets or sets student's name
 /// </summary>
 public string Name {
 get { return name; }
 set { name = value; }
 }
}

```

从(.NET Framework 3.5开始，可以缩写为自动属性：

```
public class Student {
 public string Name { get; set; }
}

```

可以限制只有本类可以私有访问：

```
public class Student {
 public string Name { get; private set; }
}

```

### Python

下例子使用的类，与一个变量、一个访问子，一个变异子。

```
class Student:
    # Initializer
    def \_\_init\_\_(self, name: str) -> None:
        # An instance variable to hold the student's name
        self.\_name = name

    # Getter method
    @property
    def name(self):
        return self.\_name

    # Setter method
    @name.setter
    def name(self, new\_name):
        self.\_name = new\_name

```

```
>>> bob = Student("Bob")
>>> bob.name 
Bob
>>> bob.name = "Alice"
>>> bob.name 
Alice
>>> bob.\_name = "Charlie" # bypass the setter
>>> bob.\_name # bypass the getter
Charlie

```

### Visual Basic.NET

类似于C#, 明确使用`Get` 与 `Set` methods. 

```
Public Class Student

    Private _name As String

    Public Property Name()
        Get
            Return _name
        End Get
        Set(ByVal value)
            _name = value
        End Set
    End Property

End Class
```

VB.NET 2010, 利用Auto Implemented创建一个属性而不必使用Get与Set语法。编译器自动创建一个隐变量，如`_name`对应于属性名`name`. 

```
Public Class Student
    Public Property name As String
End Class
```

参考文献
----

1. ↑  Stephen Fuqua.. 2009  [2009-10-19]. （原始内容存档于2011-05-13）.
2. ↑  Tim Lee.. 1998-07-13  [2021-07-09]. （原始内容存档于2016-03-05）.