常量(Constants)
常量是在编译时已知并在程序的生存期内不发生更改的不可变值。 常量使用 const 修饰符进行声明。 只有 C# 内置类型(System.Object 除外)可以声明为 const。用户定义的类型(包括类、结构和数组)不能为 const。 请使用 readonly 修饰符创建在运行时初始化一次即不可再更改的类、结构或数组。
注:内置类型表如下
常量必须在声明时初始化。例如:
C#:
class Calendar1 {
public const int months = 12;
}
在此示例中,常量 months 始终为 12,不可更改,即使是该类自身也不能更改它。 实际上,当编译器遇到 C# 源代码(例如 months)中的常量修饰符时,将直接把文本值替换到它生成的中间语言 (IL) 代码中。 因为在运行时没有与常量关联的变量地址,所以 const 字段不能通过引用传递,并且不能在表达式中作为左值出现。
字段(fields )
“字段”是直接在类或结构中声明的任何类型的变量。 字段是其包含类型的“成员”。
类或结构可以拥有实例字段(instance fields)或静态字段( static fields ),或同时拥有两者。 实例字段特定于类型的实例。 如果您拥有类 T 和实例字段 F,可以创建类型 T 的两个对象,并修改每个对象中 F 的值,这不影响另一对象中的该值。 相比之下,静态字段属于类本身,在该类的所有实例*享。 从实例 A 所做的更改将立刻呈现在实例 B 和 C 上(如果它们访问该字段)。
通常应仅为具有私有或受保护可访问性的变量使用字段。 您的类向客户端代码公开的数据应通过方法、属性和索引器提供。 通过使用这些构造间接访问内部字段,可以针对无效的输入值提供防护。 存储由公共属性公开的数据的私有字段称为“后备存储”或“支持字段”。
- 在类块中通过指定字段的访问级别,然后指定字段的类型,再指定字段的名称来声明这些字段。 例如:
C#:
public class CalendarEntry
{
// private field
private DateTime date;
// public field (Generally not recommended.)
public string day;
...
}
- 若要访问对象中的字段,请在对象名称后面添加一个句点,然后添加该字段的名称,比如 objectname.fieldname。 例如:
C#:
CalendarEntry birthday = new CalendarEntry();
birthday.day = "Saturday";
- 声明字段时可以使用赋值运算符为字段指定一个初始值。 例如,若要自动将 “Monday” 赋给 day 字段,需要声明 day,如下例所示:
C#:
public class CalendarDateWithInitialization
{
public string day = "Monday";
//...
}
- 字段可标记为 public、private、protected、internal 或 protected internal。 这些访问修饰符定义类的使用者访问字段的方式。
注:访问修饰符
可以选择将字段声明为 static。 这使得调用方在任何时候都能使用字段,即使类没有任何实例。
可以将字段声明为 readonly。 只读字段只能在初始化期间或在构造函数中赋值。 static readonly 字段非常类似于常数,只不过 C# 编译器不能在编译时访问静态只读字段的值,而只能在运行时访问。
注:变量与字段的区别与联系
- 变量代表一个存储位置,每个变量都有一个类型,该类型决定什么值可以存储在变量中,更多关于变量的介绍参考:http://csharp.net-tutorials.com/basics/variables/
- 字段是声明在类或结构中的变量,它属于类或结构的成员,更多关于字段介绍参考:http://msdn.microsoft.com/zh-cn/library/ms173118(v=vs.80).aspx
所以变量的概念是大于字段的,字段只是定义在类中或结构中的变量,例如:
C#:
public class Test
{
// field在这里是一个字段变量
public int field =0;
// 静态字段,
public static int filed2 =1;
public void TestMethod()
{
// 此时a是一个变量,并且是一个局部变量,a就不是字段了
// 而对于field 和field2我们最好认为其是一个字段,但是它也是变量,我们一般叫法为 “字段变量”
int a =2;
Console.Write(a);
}
}
方法(method)
方法是包含一系列语句的代码块。 程序通过调用该方法并指定任何所需的方法参数使语句得以执行。 在 C# 中,每个执行的指令均在方法的上下文中执行。 Main 方法是每个 C# 应用程序的入口点,并在启动程序时由公共语言运行时 (CLR) 调用。
方法签名
- 通过指定访问级别(如 public 或 private)、可选修饰符(如 abstract 或 sealed)、返回值、方法的名称以及任何方法参数,在类或结构中声明方法。 这些部件一起构成方法的签名。
- 方法参数在括号内,并且用逗号分隔。 空括号指示方法不需要任何参数。
注:出于方法重载的目的,方法的返回类型不是方法签名的一部分。 但是在确定委托和它所指向的方法之间的兼容性时,它是方法签名的一部分。
补充:方法重载是指在一个类中定义多个同名的方法,但要求每个方法具有不同的参数的类型或参数的个数。比如:
public void Func(){}
public void Func(int a){}
public void Func(int a,int b){}
···
Func(a);//会调用public void Func(int a)这个版本
具体规范:
- 方法名一定要相同。
- 方法的参数表必须不同,包括参数的类型或个数,以此区分不同的方法体。(如果参数个数不同,就不管它的参数类型了。如果参数个数相同,那么参数的类型或者参数的顺序必须不同。)
- 方法的返回类型、修饰符可以相同,也可不同。即在C#中不能只根据返回值的类型不同来区分不同的重载函数。
方法访问
调用对象上的方法就像访问字段。 在对象名之后添加一个句点、方法名和括号。 参数列在括号里,并且用逗号分隔。
方法参数与参数
该方法定义指定任何所需参数的名称和类型。调用代码调用该方法时,它为每个参数提供了称为参数的具体值。 参数必须与参数类型兼容,但调用代码中使用的参数名(如果有)不需要与方法中定义的参数名相同。
按引用传递与按值传递
默认情况下,值类型传递给方法时,传递的是副本而不是对象本身。 因此,对参数的更改不会影响调用方法中的原始副本。可以使用 ref 关键字按引用传递值类型。
引用类型的对象传递到方法中时,将传递对对象的引用。 也就是说,该方法接收的不是对象本身,而是指示该对象位置的参数。 如果通过使用此引用更改对象的成员,即使是按值传递该对象,此更改也会反映在调用方法的参数中。
通过使用 class 关键字创建引用类型,如以下示例所示。
public class SampleRefType
{
public int value;
}
现在,如果将基于此类型的对象传递到方法,则将传递对对象的引用。 下面的示例将 SampleRefType 类型的对象传递到 ModifyObject 方法。
public static void TestRefType()
{
SampleRefType rt = new SampleRefType();
rt.value = 44;
ModifyObject(rt);
Console.WriteLine(rt.value);
}
static void ModifyObject(SampleRefType obj)
{
obj.value = 33;
}
该示例执行的内容实质上与先前示例相同,均按值将参数传递到方法。 但是因为使用了引用类型,结果有所不同。 ModifyObject 中所做的对形参 obj 的 value 字段的修改,也会更改 TestRefType 方法中实参 rt 的 value 字段。 TestRefType 方法显示 33 作为输出。
返回值
方法可以将值返回到调用方。如果列在方法名之前的返回类型不是 void,则该方法可通过使用 return 关键字返回值。带 return 关键字,后跟与返回类型匹配的值的语句将该值返回到方法调用方。return 关键字还会停止执行该方法。如果返回类型为 void,没有值的 return 语句仍可用于停止执行该方法。 没有 return 关键字,当方法到达代码块结尾时,将停止执行。具有非空的返回类型的方法都需要使用 return 关键字来返回值。例如,这两种方法都使用 return 关键字来返回整数:
class SimpleMath
{
public int AddTwoNumbers(int number1, int number2)
{
return number1 + number2;
}
public int SquareANumber(int number)
{
return number * number;
}
}
若要使用从方法返回的值,调用方法可以在相同类型的值足够的地方使用该方法调用本身。 也可以将返回值分配给变量。 例如,以下两个代码示例实现了相同的目标:
int result = obj.AddTwoNumbers(1, 2);
result = obj.SquareANumber(result);
// The result is 9.
Console.WriteLine(result);
result = obj.SquareANumber(obj.AddTwoNumbers(1, 2));
// The result is 9.
Console.WriteLine(result);
属性(property)
属性是一种成员,它提供灵活的机制来读取、写入或计算私有字段的值。 属性可用作公共数据成员,但它们实际上是称为“访问器”的特殊方法。 这使得可以轻松访问数据,还有助于提高方法的安全性和灵活性。
属性结合了字段和方法的多个方面。 对于对象的用户,属性显示为字段,访问该属性需要相同的语法。 对于类的实现者,属性是一个或两个代码块,表示一个 get 访问器和/或一个 set 访问器。 当读取属性时,执行 get 访问器的代码块;当向属性分配一个新值时,执行 set 访问器的代码块。 不具有 set 访问器的属性被视为只读属性。 不具有 get 访问器的属性被视为只写属性。 同时具有这两个访问器的属性是读写属性。
与字段不同,属性不作为变量来分类。 因此,不能将属性作为 ref(C# 参考)参数或 out(C# 参考)参数传递。
属性具有多种用法:它们可在允许更改前验证数据;它们可透明地公开某个类上的数据,该类的数据实际上是从其他源(例如数据库)检索到的;当数据被更改时,它们可采取行动,例如引发事件或更改其他字段的值。
属性在类块中是按以下方式来声明的:指定字段的访问级别,接下来指定属性的类型和名称,然后跟上声明 get 访问器和/或 set 访问器的代码块。 例如:
public class Date
{
private int month = 7; // Backing store
public int Month
{
get
{
return month;
}
set
{
if ((value > 0) && (value < 13))
{
month = value;
}
}
}
}
在此示例中,Month 是作为属性声明的,这样 set 访问器可确保 Month 值设置为 1 和 12 之间。 Month 属性使用私有字段来跟踪该实际值。 属性的数据的真实位置经常称为属性的“后备存储”。属性使用作为后备存储的私有字段是很常见的。 将字段标记为私有可确保该字段只能通过调用属性来更改。
get 访问器
get 访问器体与方法体相似。 它必须返回属性类型的值。 执行 get 访问器相当于读取字段的值。
以下是返回私有字段 name 的值的 get 访问器:
class Person {
private string name; // the name field
public string Name // the Name property
{
get
{
return name;
}
}
}
当引用属性时,除非该属性为赋值目标,否则将调用 get 访问器以读取该属性的值。 例如:
Person person = new Person();
//...
System.Console.Write(person.Name); // the get accessor is invoked here
get 访问器必须以 return 或 throw 语句终止,并且控制权不能离开访问器体。
通过使用 get 访问器更改对象的状态不是一种好的编程风格。 例如,以下访问器在每次访问 number 字段时都会产生更改对象状态的副作用。
private int number;
public int Number
{
get
{
return number++; // Don't do this
}
}
get 访问器可用于返回字段值,或用于计算并返回字段值。 例如:
class Employee
{
private string name;
public string Name
{
get
{
return name != null ? name : "NA";
}
}
}
在上一个代码段中,如果不对 Name 属性赋值,它将返回值 NA。
set 访问器
set 访问器类似于返回类型为 void 的方法。 它使用称为 value 的隐式参数,此参数的类型是属性的类型。 在下面的示例中,将 set 访问器添加到 Name 属性:
class Person
{
private string name; // the name field
public string Name // the Name property
{
get
{
return name;
}
set
{
name = value;
}
}
}
当对属性赋值时,用提供新值的参数调用 set 访问器。 例如:
Person person = new Person();
person.Name = "Joe"; // the set accessor is invoked here
System.Console.Write(person.Name); // the get accessor is invoked here
在 set 访问器中,对局部变量声明使用隐式参数名称 value 是错误的。
备注
- 可将属性标记为 public、private、protected、internal 或 protected internal。 这些访问修饰符定义类的用户如何才能访问属性。 同一属性的 get 和 set 访问器可能具有不同的访问修饰符。 例如,get 可能是 public 以允许来自类型外的只读访问;set 可能是 private 或 protected。 有关更多信息,请参见 访问修饰符(C# 编程指南)。
- 可以使用 static 关键字将属性声明为静态属性。 这使得调用方随时可使用该属性,即使不存在类的实例。 有关更多信息,请参见 静态类和静态类成员(C# 编程指南)。
- 可以使用 virtual 关键字将属性标记为虚属性。 这样,派生类就可以通过使用 override 关键字来重写事件行为。 有关这些选项的更多信息,请参见 继承(C# 编程指南)。
- 重写虚属性的属性还可以是 sealed 的,这表示它对派生类不再是虚拟的。 最后,可以将属性声明为 abstract。 这意味着类中没有任何实现,派生类必须编写自己的实现。 有关这些选项的更多信息,请参见 抽象类、密封类及类成员(C# 编程指南)。
- 对 static 属性的访问器使用 virtual、abstract或 override修饰符是错误的。
索引器(Indexers)
- 索引器允许类或结构的实例就像数组一样进行索引。 索引器类似于属性,不同之处在于它们的取值函数采用参数。
- 使用索引器可以用类似于数组的方式为对象建立索引。
- get 取值函数返回值。 set 取值函数分配值。
- this 关键字用于定义索引器。
- value 关键字用于定义由 set 索引器分配的值。
- 索引器不必根据整数值进行索引;由你决定如何定义 特定的查找机制。
- 索引器可被重载。
- 索引器可以有多个形参,例如当访问二维数组时。
在下面的示例中,定义了一个泛型类,并为其提供了简单的 get 和 set 取值函数方法(作为分配和检索值的方法)。 Program 类创建了此类的一个实例,用于存储字符串。
class SampleCollection<T>
{
// Declare an array to store the data elements.
private T[] arr = new T[100];
// Define the indexer, which will allow client code
// to use [] notation on the class instance itself.
// (See line 2 of code in Main below.)
public T this[int i]
{
get
{
// This indexer is very simple, and just returns or sets
// the corresponding element from the internal array.
return arr[i];
}
set
{
arr[i] = value;
}
}
}
// This class shows how client code uses the indexer.
class Program
{
static void Main(string[] args)
{
// Declare an instance of the SampleCollection type.
SampleCollection<string> stringCollection = new SampleCollection<string>();
// Use [] notation on the type.
stringCollection[0] = "Hello, World";
System.Console.WriteLine(stringCollection[0]);
}
}
// Output:
// Hello, World.
参考资料
- 常量(C# 编程指南):https://msdn.microsoft.com/zh-cn/library/ms173119.aspx
- 字段(C# 编程指南):https://msdn.microsoft.com/zh-cn/library/ms173118.aspx
- 方法(C# 编程指南):https://msdn.microsoft.com/zh-cn/library/ms173114.aspx
- 使用属性(C# 编程指南):https://msdn.microsoft.com/zh-cn/library/w86s7x04.aspx
- 索引器(C# 编程指南):https://msdn.microsoft.com/zh-cn/library/6x16t2tx.aspx#bkmk_relatedsections