- class X {…}
-
- var instance = new X(…);
C#中,数据成员称为字段。与具体对象相关的字段称为实例字段;实例字段允许在声明时初始化,初始化语句在类构造函数前执行, 例如:
- class Employee
- {
- public string FirstName;
- public string LastName;
- public string Salary = "Not enough";
- public Employee()
- {
- Salray= string.Empty;
- }
- }
实例字段只能从对象中访问,例如:
- public static void Main()
- {
- Employee employee1 = new Employee();
- Employee employee2;
- employee2 = new Employee();
- employee1.FirstName = "Inigo";
- employee1.LastName = "Montoya";
- employee1.Salary = "Too Little";
- IncreaseSalary(employee1);
- Console.WriteLine( "{0} {1}: {2}",employee1.FirstName, employee1.LastName,employee1.Salary);
- }
- static void IncreaseSalary(Employee employee)
- {
- employee.Salary = "Enough to survive on";
- }
C#中, 只能通过对象调用的成员方法称为实例方法。
在类的实例成员内部,可以使用this 获得调用实例成员的对象引用,例如:
- class Employee
- {
- public string FirstName;
- public string LastName;
- public string Salary;
- public string GetName()
- {
- return $"{ FirstName } { LastName }";
- }
- public void SetName(string newFirstName, string newLastName)
- {
- this.FirstName = newFirstName;
- this.LastName = newLastName;
- }
- }
this关键字也能用来显式调用实例方法 或在方法调用中传递 ,例如:
- class Employee
- {
- public string FirstName;
- public string LastName;
- public string Salary;
- public string GetName() => $"{ FirstName } { LastName }";
- public void SetName(string newFirstName, string newLastName)
- {
- this.FirstName = newFirstName;
- this.LastName = newLastName;
- Console.WriteLine( $"Name changed to '{ this.GetName() }'");
- }
- public void Save()
- {
- DataStorage.Store(this);
- }
- }
- class DataStorage
- {
- // Save an employee object to a file named with the Employee name.
- public static void Store(Employee employee) { ...}
- }
访问修饰符标识了所修饰成员的封装级别。
- class Employee
- {
- public string FirstName, LastName,Salary, Password;
- private bool IsAuthenticated;
- public bool Logon(string password)
- {
- if (Password == password)
- IsAuthenticated = true;
- return IsAuthenticated;
- }
- public bool GetIsAuthenticated() => IsAuthenticated;
- // ...
- }
类型缺省访问级别:
| Members of | Default member accessibility | Allowed declared accessibility of the member |
| enum | public | None |
| class | private | public protected internal private protected internal |
| interface | public | None |
| struct | private | public internal private |
属性结合了字段和成员方法的特点。 对于对象的用户来说,属性似乎是一个字段,访问属性使用与访问字段 相同的语法。 对于类的实现者来说,属性是由 get 访问器和/或 set 访问器组成代码块。读取属性时,执行 get 访问器的代码块;向属性赋值时,执行 set 访问器的代码块。
不含 set 访问器的属性称为只读属性。 将不含 get 访问器的属性称为只写属性。同时具有以上两个访问器的属性称为读写属性。
与字段不同,属性不会被归类为变量。 因此,不能将属性作为 ref 或 out 参数传递。
在 C# 3.0及更高版本,当属性访问器中不需要任何其他逻辑时,自动实现的属性会使属性声明更加简洁。在 C# 6和更高版本中,可以像字段一样初始化自动实现属性。
- public static void Main()
- {
- Employee employee1 = new Employee();
- Employee employee2 = new Employee();
- employee1.FirstName = "Inigo"; // Call the FirstName property's setter.
- System.Console.WriteLine(employee1.FirstName); // Call the FirstName property's getter.
- // Assign an auto-implemented property
- employee2.Title = "Computer Nerd";
- employee1.Manager = employee2;
- // Print employee1's manager's title.
- System.Console.WriteLine(employee1.Manager.Title);
- }
- class Employee
- {
- public string FirstName { get; set; }
- private string LastName { get; set; }
- public string Title { get; set; }
- public Employee Manager { get; set; }
- public string Salary { get; set; } = "Not Enough";
- }
缺省情况下,get /set 访问器具有相同的可见性和访问级别。从C# 2.0开始,在属性实现中允许为get 或set 部分指定访问修饰符,从而覆盖为属性指定的访问修饰符
对属性 使用访问修饰符有以下限制:
- class Employee
- {
- public void Initialize(int id) => Id = id.ToString();
- public string Id
- {
- get
- {
- return _Id;
- }
- private set
- {
- // Providing an access modifier is possible in C# 2.0 and higher only
- _Id = value;
- }
- }
- private string _Id;
- }
- class Employee
- {
- public Employee(string firstName, string lastName) // constructor
- {
- FirstName = firstName;
- LastName = lastName;
- }
- public string FirstName { get; set; }
- public string LastName { get; set; }
- public string Title {get; set}
- public string Salary { get; set; } = "Not Enough";
- public string Name
- {
- get
- {
- return FirstName + " " + LastName;
- }
- set
- {
- string[] names;
- names = value.Split(new char[] { ' ' });
- if (names.Length == 2)
- {
- FirstName = names[0];
- LastName = names[1];
- }
- else
- {
- throw new System.ArgumentException(string.Format($"Assigned value '{ value }' is invalid", nameof(value)));
- }
- }
- }
- }
- public static void Main()
- {
- Employee employee;
- employee = new Employee("Inigo", "Montoya");
- employee.Salary = "Too Little";
- Console.WriteLine( "{0} {1}: {2}", employee.FirstName,
- employee.LastName,employee.Salary);
- }
如果类没有显式定义构造器,C# 编译器会在编译时自动添加一个不含任何参数的构造函数。一旦类显定义构造器,编译器就不会提供默认构造函数。
初始化器用于初始化对象中所有可以访问的字段和属性。在调用构造器时,可以在后面的大括号中添加成员初始化列表,例如:
- public static void Main()
- {
- Employee employee = new Employee("Inigo", "Montoya")
- {
- Title = "Computer Nerd",
- Salary = "Not enough"
- };
- Console.WriteLine("{0} {1} ({2}): {3}", employee.FirstName, employee.LastName, employee.Title, employee.Salary);
- }
C# 中,允许从一个构造器中调用同一个类的另一个构造器, 方法是在一个冒号后添加this关键字,再添加被调用构造器的参数列表,例如:
- class Employee
- {
- public Employee(string firstName, string lastName)
- {
- FirstName = firstName;
- LastName = lastName;
- }
- public Employee(int id, string firstName, string lastName)
- : this(firstName, lastName)
- {
- Id = id;
- }
-
- public Employee(int id)
- {
- Id = id;
- // NOTE: Member constructors cannot be called explicitly inline
- // this(id, firstName, lastName);
- }
- public int Id { get; private set; }
- public string FirstName { get; set; }
- public string LastName { get; set; }
- public string Salary { get; set; } = "Not Enough";
- }
匿名类型是编译器动态生成的类型,编译器遇到匿名类型时,会自动生成一个CIL类。该类具有与匿名类型声明中已经命名的值和数据类型对应的属性。例如:
- public static void Main()
- {
- var patent1 =new
- {
- Title = "Bifocals",
- YearOfPublication = "1784"
- };
- var patent2 =new
- {
- Title = "Phonograph",
- YearOfPublication = "1877"
- };
- var patent3 =new
- {
- patent1.Title,
- Year = patent1.YearOfPublication
- };
- System.Console.WriteLine("{0} ({1})",patent1.Title, patent1.YearOfPublication);
- System.Console.WriteLine("{0} ({1})", patent2.Title, patent1.YearOfPublication);
- Console.WriteLine();
- Console.WriteLine(patent1);
- Console.WriteLine(patent2);
-
- Console.WriteLine();
- Console.WriteLine(patent3);
- }
在类的多个实例之间共享的字段,用static 关键字标识。和实例字段一样,静态字段也可以在声明时初始化。例如:
- class Employee
- {
- // ...
- public static int Id; // default(int): 0
- public static int NextId = 42;
- // ...
- }
和实例字段不一样,未初始化的静态字段将获得默认值,即 default(T)的结果
和静态字段类似,静态方法也用static关键字标识。静态方法可以通过类名直接访问。例如:
- public static void Main()
- {
- DirectoryInfo directory = new DirectoryInfo(".\\Source");
- directory.MoveTo(".\\Root");
- DirectoryInfoExtension.CopyTo(directory, ".\\Target", SearchOption.AllDirectories, "*");
- }
- public static class DirectoryInfoExtension
- {
- public static void CopyTo( DirectoryInfo sourceDirectory, string target, SearchOption option, string searchPattern)
- {
- if (target[target.Length - 1] != Path.DirectorySeparatorChar)
- target += Path.DirectorySeparatorChar;
- if (!Directory.Exists(target))
- Directory.CreateDirectory(target);
- for (int i = 0; i < searchPattern.Length; i++)
- {
- foreach (string file in Directory.GetFiles(sourceDirectory.FullName, searchPattern))
- {
- File.Copy(file, target + Path.GetFileName(file), true);
- }
- }
- if (option == SearchOption.AllDirectories) //Copy subdirectories (recursively)
- {
- foreach (string element in Directory.GetDirectories(sourceDirectory.FullName))
- Copy(element, target + Path.GetFileName(element),searchPattern);
- }
- }
- private static void Copy(string element, string fileName, string searchPattern)
- {
- Console.WriteLine("Copying " + fileName);
- }
- }
静态构造器不显式调用,而是在运行时在首次访问类时自动调用。首次访问类发生在条用普通构造器时,也可能发生在访问类的静态方法或字段。静态构造器不允许带任何参数
- class Employee
- {
- static Employee()
- {
- Random randomGenerator = new Random();
- NextId = randomGenerator.Next(101, 999);
- }
-
- // ...
- public static int NextId = 42;
- // ...
- }
属性也能static。例如:
- class Employee
- {
- // ...
- public static int NextId
- {
- get
- {
- return _NextId;
- }
- private set
- {
- _NextId = value;
- }
- }
- public static int _NextId = 42;
- // ...
- }
C#中也能定义静态类。静态类不含任何实例字段或方法。因此静态类不能实例化。编译器自动在CIL 代码中将静态类标记为abstract 和sealed。即将类指定为不可扩展
- public static class SimpleMath
- {
- public static int Max(params int[] numbers)
- {
-
- if (numbers.Length == 0) // Check that there is at least one item in numbers.
- throw new ArgumentException( "numbers cannot be empty", nameof(numbers));
- int result = numbers[0];
- foreach (int number in numbers)
- {
- if (number > result)
- result = number;
- }
- return result;
- }
- }
- public class Program
- {
- public static void Main(string[] args)
- {
- int[] numbers = new int[args.Length];
- for (int count = 0; count < args.Length; count++)
- numbers[count] = args[count].Length;
- Console.WriteLine( $@"Longest argument length = { SimpleMath.Max(numbers) }");
- }
- }
- class ConvertUnits
- {
- public const float CentimersPerInch = 2.54F;
- public const int CupsPerGallon = 16;
- }
readonly 修饰符只能用于字段(不能用于局部变量)。它指出字段值只能从构造器中更改或声明时通过初始化器更改。
- class Employee
- {
- public Employee(int id)
- {
- _Id = id;
- }
- private readonly int _Id;
- public int Id{
- get { return _Id; }
- }
- // Error: A readonly field cannot be assigned to (excep in a constructor or a variable initializer)
- // public void SetId(int id) =>_Id = id;
- }
分部类是一个类的多个部分, 这些部分可以合并成一个完整的类。分部类主要用于将一个类的定义划分到多个文件中。 C# 使用关键字partial来声明分部类
- // File: Program1.cs
- partial class Program
- {
- }
- // File: Program2.cs
- partial class Program
- {
- }
分部方法存在于分部类中,它允许在一个文件中声明方法,而在另一文件中实现该方法。例如:
- // File: Person.Designer.cs
- public partial class Person
- {
- #region Extensibility Method Definitions
- partial void OnLastNameChanging(string value);
- partial void OnFirstNameChanging(string value);
- #endregion
- // ...
- }
- // File: Person.cs
- partial class Person
- {
- partial void OnLastNameChanging(string value)
- {
- //...
- }
- partial void OnFirstNameChanging(string value)
- {
- //...
- }
- }