以前是学C++的,初次学微软的C#头都大了。什么字段,常量,属性,方法......微软把别人的东西拿来糅合在C#里,弄成了一个“大杂烩”。其实,说到底,“字段”不就是“变量”吗,所谓的“方法”不就是“函数”吗,故弄玄虚!从MSDN上弄来了详细的介绍,看下面:
字段:
“字段”是直接在类或结构中声明的任何类型的变量。 字段是其包含类型的“成员”。
类或结构可以拥有实例字段或静态字段,或同时拥有两者。 实例字段特定于类型的实例。 如果您拥有类 T 和实例字段 F,可以创建类型 T 的两个对象,并修改每个对象中 F 的值,这不影响另一对象中的该值。 相比之下,静态字段属于类本身,在该类的所有实例中共享。 从实例 A 所做的更改将立刻呈现在实例 B 和 C 上(如果它们访问该字段)。
通常应仅为具有私有或受保护可访问性的变量使用字段。 您的类向客户端代码公开的数据应通过方法、属性和索引器提供。 通过使用这些构造间接访问内部字段,可以针对无效的输入值提供防护。 存储由公共属性公开的数据的私有字段称为“后备存储”或“支持字段”。
字段通常存储这样的数据:该数据必须可供多个类方法访问,并且其存储期必须长于任何单个方法的生存期。 例如,表示日历日期的类可能有三个整数字段:一个表示月份,一个表示日期,还有一个表示年份。 不在单个方法范围外部使用的变量应在方法体自身范围内声明为局部变量。
在类块中通过指定字段的访问级别,然后指定字段的类型,再指定字段的名称来声明这些字段。 例如:
1 public class CalendarEntry 2 { 3 // private field 4 private DateTime date; 5 6 // public field (Generally not recommended.) 7 public string day; 8 9 // Public property exposes date field safely. 10 public DateTime Date 11 { 12 get 13 { 14 return date; 15 } 16 set 17 { 18 // Set some reasonable boundaries for likely birth dates. 19 if (value.Year > 1900 && value.Year <= DateTime.Today.Year) 20 { 21 date = value; 22 } 23 else 24 throw new ArgumentOutOfRangeException(); 25 } 26 27 } 28 29 // Public method also exposes date field safely. 30 // Example call: birthday.SetDate("1975, 6, 30"); 31 public void SetDate(string dateString) 32 { 33 DateTime dt = Convert.ToDateTime(dateString); 34 35 // Set some reasonable boundaries for likely birth dates. 36 if (dt.Year > 1900 && dt.Year <= DateTime.Today.Year) 37 { 38 date = dt; 39 } 40 else 41 throw new ArgumentOutOfRangeException(); 42 } 43 44 public TimeSpan GetTimeSpan(string dateString) 45 { 46 DateTime dt = Convert.ToDateTime(dateString); 47 48 if (dt != null && dt.Ticks < date.Ticks) 49 { 50 return date - dt; 51 } 52 else 53 throw new ArgumentOutOfRangeException(); 54 55 } 56 }
若要访问对象中的字段,请在对象名称后面添加一个句点,然后添加该字段的名称,比如 objectname.fieldname。 例如:
1 CalendarEntry birthday = new CalendarEntry(); 2 birthday.day = "Saturday";
声明字段时可以使用赋值运算符为字段指定一个初始值。 例如,若要自动将 "Monday" 赋给 day 字段,需要声明 day,如下例所示:
public class CalendarDateWithInitialization { public string day = "Monday"; //... }
字段的初始化紧靠调用对象实例的构造函数之前。 如果构造函数为字段赋值,则该值将覆盖字段声明期间给出的任何值。
字段可标记为 public、private、protected、internal 或 protected internal。 这些访问修饰符定义类的使用者访问字段的方式。 有关更多信息,请参见 访问修饰符(C# 编程指南)。
可以选择将字段声明为 static。 这使得调用方在任何时候都能使用字段,即使类没有任何实例。 有关更多信息,请参见 静态类和静态类成员(C# 编程指南)。
可以将字段声明为 readonly。 只读字段只能在初始化期间或在构造函数中赋值。 static readonly 字段非常类似于常数,只不过 C# 编译器不能在编译时访问静态只读字段的值,而只能在运行时访问。 有关更多信息,请参见 常量(C# 编程指南)。
常量:
常量是在编译时已知并在程序的生存期内不发生更改的不可变值。 常量使用 const 修饰符进行声明。 只有 C# 内置类型(System.Object 除外)可以声明为 const。 有关内置类型的列表,请参见内置类型表(C# 参考)。 用户定义的类型(包括类、结构和数组)不能为 const。 请使用 readonly 修饰符创建在运行时初始化一次即不可再更改的类、结构或数组。
C# 不支持 const 方法、属性或事件。
可以使用枚举类型为整数内置类型(例如 int、uint、long 等等)定义命名常量。 有关更多信息,请参见 enum(C# 参考)。
常量必须在声明时初始化。 例如:
1 class Calendar1 2 { 3 public const int months = 12; 4 }
在此示例中,常量 months 始终为 12,不可更改,即使是该类自身也不能更改它。 实际上,当编译器遇到 C# 源代码(例如 months)中的常量修饰符时,将直接把文本值替换到它生成的中间语言 (IL) 代码中。 因为在运行时没有与常量关联的变量地址,所以 const 字段不能通过引用传递,并且不能在表达式中作为左值出现。
可以同时声明多个相同类型的常量,例如:
class Calendar2 { const int months = 12, weeks = 52, days = 365; }
如果不会造成循环引用,用于初始化一个常量的表达式可以引用另一个常量。 例如:
1 class Calendar3 2 { 3 const int months = 12; 4 const int weeks = 52; 5 const int days = 365; 6 7 const double daysPerWeek = (double) days / (double) weeks; 8 const double daysPerMonth = (double) days / (double) months; 9 }
常量可标记为 public、private、protected、internal 或 protectedinternal。 这些访问修饰符定义类的用户访问该常量的方式。 有关更多信息,请参见 访问修饰符(C# 编程指南)。
因为常量值对该类型的所有实例是相同的,所以常量被当作 static 字段一样访问。 不使用 static 关键字声明常量。 未包含在定义常量的类中的表达式必须使用类名、一个句点和常量名来访问该常量。 例如:
int birthstones = Calendar.months;
属性:
属性是这样的成员:它提供灵活的机制来读取、编写或计算某个私有字段的值。 可以像使用公共数据成员一样使用属性,但实际上它们是称作“访问器”的特殊方法。 这使得可以轻松访问数据,此外还有助于提高方法的安全性和灵活性。
在本示例中,TimePeriod 类存储一个时间段。 在内部,类以秒为单位存储时间,但客户端使用名为 Hours 的属性能够以小时为单位指定时间。 Hours 属性的访问器执行小时和秒之间的转换。
1 class TimePeriod 2 { 3 private double seconds; 4 5 public double Hours 6 { 7 get { return seconds / 3600; } 8 set { seconds = value * 3600; } 9 } 10 } 11 12 13 class Program 14 { 15 static void Main() 16 { 17 TimePeriod t = new TimePeriod(); 18 19 // Assigning the Hours property causes the 'set' accessor to be called. 20 t.Hours = 24; 21 22 // Evaluating the Hours property causes the 'get' accessor to be called. 23 System.Console.WriteLine("Time in hours: " + t.Hours); 24 } 25 } 26 // Output: Time in hours: 24
-
属性使类能够以一种公开的方法获取和设置值,同时隐藏实现或验证代码。
-
get 属性访问器用于返回属性值,而 set 访问器用于分配新值。 这些访问器可以有不同的访问级别。 有关更多信息,请参见 限制访问器可访问性(C# 编程指南)。
-
value 关键字用于定义由 set 取值函数分配的值。
-
不实现 set 取值函数的属性是只读的。
-
对于不需要任何自定义访问器代码的简单属性,可考虑选择使用自动实现的属性。 有关更多信息,请参见自动实现的属性(C# 编程指南)。
方法:
“方法”是包含一系列语句的代码块。 程序通过“调用”方法并指定所需的任何方法参数来执行语句。 在 C# 中,每个执行指令都是在方法的上下文中执行的。 Main 方法是每个 C# 应用程序的入口点,在启动程序时由公共语言运行时 (CLR) 调用。
通过指定方法的访问级别(例如 public 或 private)、可选修饰符(例如 abstract 或 sealed)、返回值、名称和任何方法参数,可以在类或结构中声明方法。 这些部分统称为方法的“签名”。
方法参数括在括号中,并用逗号隔开。 空括号表示方法不需要参数。 下面的类包含三个方法:
1 abstract class Motorcycle 2 { 3 // Anyone can call this. 4 public void StartEngine() {/* Method statements here */ } 5 6 // Only derived classes can call this. 7 protected void AddGas(int gallons) { /* Method statements here */ } 8 9 // Derived classes can override the base class implementation. 10 public virtual int Drive(int miles, int speed) { /* Method statements here */ return 1; } 11 12 // Derived classes must implement this. 13 public abstract double GetTopSpeed(); 14 }
在对象上调用方法类似于访问字段。 在对象名称之后,依次添加句点、方法名称和括号。 参数在括号内列出,并用逗号隔开。 因此,可以按以下示例中的方式调用 Motorcycle类的方法:
1 class TestMotorcycle : Motorcycle 2 { 3 4 public override double GetTopSpeed() 5 { 6 return 108.4; 7 } 8 9 static void Main() 10 { 11 12 TestMotorcycle moto = new TestMotorcycle(); 13 14 moto.StartEngine(); 15 moto.AddGas(15); 16 moto.Drive(5, 20); 17 double speed = moto.GetTopSpeed(); 18 Console.WriteLine("My top speed is {0}", speed); 19 } 20 }
方法定义指定所需任何“形参”的名称和类型。 调用代码在调用方法时,将为每个形参提供称为“实参”的具体值。 实参必须与形参类型兼容,但调用代码中使用的实参名称(如果有)不必与方法中定义的形参名称相同。 例如:
1 public void Caller() 2 { 3 int numA = 4; 4 // Call with an int variable. 5 int productA = Square(numA); 6 7 int numB = 32; 8 // Call with another int variable. 9 int productB = Square(numB); 10 11 // Call with an integer literal. 12 int productC = Square(12); 13 14 // Call with an expression that evaulates to int. 15 productC = Square(productA * 3); 16 } 17 18 int Square(int i) 19 { 20 // Store input argument in a local variable. 21 int input = i; 22 return input * input; 23 }
默认情况下,将值类型传递给方法时,传递的是副本而不是对象本身。 因此,对参数所做的更改对于调用方法中的原始副本没有影响。 可以使用 ref 关键字通过引用传递值类型。 有关更多信息,请参见传递值类型参数(C# 编程指南)。 有关内置值类型的列表,请参见值类型表(C# 参考)。
当引用类型的对象传递给方法时,对对象的引用传递。 即方法接收不是对象,还指示对象的位置的参数。 如果更改对象的成员通过使用该引用,更改反映在被调用的方法的参数,因此,即使通过对象的值。
如下面的示例所示,通过使用 class 关键字,则创建一个引用类型。
public class SampleRefType { public int value; }
现在,因此,如果您通过根据方法的此类型的对象,对对象的引用传递。 下面的示例通过类型 SampleRefType 对象传递给方法 ModifyObject。
1 public static void TestRefType() 2 { 3 SampleRefType rt = new SampleRefType(); 4 rt.value = 44; 5 ModifyObject(rt); 6 Console.WriteLine(rt.value); 7 } 8 static void ModifyObject(SampleRefType obj) 9 { 10 obj.value = 33; 11 }
该示例实质上执行操作与前例同样因为它通过值传递实参传递给方法。 但,使用引用类型,则结果是不同的。 在对该参数进行 value 字段的 ModifyObject 做的修改,obj,也会更改参数,rt的 value 字段,在 TestRefType 方法。 TestRefType 方法显示 33 作为输出。
方法可以向调用方返回值。 如果返回类型(方法名称前列出的类型)不是 void,则方法可以使用 return 关键字来返回值。 如果语句中 return 关键字的后面是与返回类型匹配的值,则该语句将该值返回给方法调用方。 return 关键字还会停止方法的执行。 如果返回类型为 void,则可使用没有值的 return 语句来停止方法的执行。 如果没有return 关键字,方法执行到代码块末尾时即会停止。 具有非 void 返回类型的方法才能使用 return 关键字返回值。 例如,下面的两个方法使用 return 关键字来返回整数:
1 class SimpleMath 2 { 3 public int AddTwoNumbers(int number1, int number2) 4 { 5 return number1 + number2; 6 } 7 8 public int SquareANumber(int number) 9 { 10 return number * number; 11 } 12 }