一、类
1、类的概念
-
类是现实世界概念的抽象:封装、继承、多态
-
数据成员: 类中存储数据的变量
-
成员方法: 类中操纵数据成员的函数称为成员方法
-
对象:类的实例
-
类定义
class X {...}
var instance = new X(...);
2、实例字段
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) { ...}
}
3、访问修饰符
访问修饰符标识了所修饰成员的封装级别。
-
public:类或成员修饰符;表明类或成员可以从类外部访问
-
private:成员修饰符; 表明修饰的成员仅在声明的类内部访问
-
protected:成员修饰符; 表明修饰的成员仅在声明的类或派生类内部访问
-
internal:类或成员修饰符;表明类或成员仅能在相同程序集内部访问
-
Protected internal:类或成员修饰符;表明类或成员仅能在当前程序集内部或派生类访问
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;
// ...
}
4、方法的参数
类型缺省访问级别:
|------------|------------------------------|------------------------------------------------------|
| 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 |
5、属性
属性结合了字段和成员方法的特点。 对于对象的用户来说,属性似乎是一个字段,访问属性使用与访问字段 相同的语法。 对于类的实现者来说,属性是由 get 访问器和/或 set 访问器组成代码块。读取属性时,执行 get 访问器的代码块;向属性赋值时,执行 set 访问器的代码块。
不含 set 访问器的属性称为只读属性。 将不含 get 访问器的属性称为只写属性。同时具有以上两个访问器的属性称为读写属性。
与字段不同,属性不会被归类为变量。 因此,不能将属性作为 ref 或 out 参数传递。
6、自动实现属性
在 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";
}
7、属性的访问限制
缺省情况下,get /set 访问器具有相同的可见性和访问级别。从C# 2.0开始,在属性实现中允许为get 或set 部分指定访问修饰符,从而覆盖为属性指定的访问修饰符
对属性 使用访问修饰符有以下限制:
-
不能对接口或显式实现的接口成员使用访问修饰符。
-
仅当属性同时包含 set 和 get 访问器时,才能使用访问器修饰符。 这种情况下,只允许对其中之一使用修饰符。
-
如果属性或索引器具有 override 修饰符,则访问器修饰符必须与重载的访问器的访问修饰符(如有)匹配。
-
访问器的可访问性级别必须比属性本身的可访问性级别具有更严格的限制。
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;
}
二、构造器
1、构造器
1.1 构造器是与类名相同,没有返回值的方法, 例如:
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)));
}
}
}
}
1.2 调用构造器
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);
}
1.3 默认构造器
如果类没有显式定义构造器,C# 编译器会在编译时自动添加一个不含任何参数的构造函数。一旦类显定义构造器,编译器就不会提供默认构造函数。
2、对象初始化器
初始化器用于初始化对象中所有可以访问的字段和属性。在调用构造器时,可以在后面的大括号中添加成员初始化列表,例如:
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);
}
3、构造器链
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";
}
4、匿名类型
匿名类型是编译器动态生成的类型,编译器遇到匿名类型时,会自动生成一个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);
}
三、静态成员
1、静态字段
在类的多个实例之间共享的字段,用static 关键字标识。和实例字段一样,静态字段也可以在声明时初始化。例如:
class Employee
{
// ...
public static int Id; // default(int): 0
public static int NextId = 42;
// ...
}
和实例字段不一样,未初始化的静态字段将获得默认值,即 default(T)的结果
2、静态方法
和静态字段类似,静态方法也用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);
}
}
3、静态构造器
静态构造器不显式调用,而是在运行时在首次访问类时自动调用。首次访问类发生在条用普通构造器时,也可能发生在访问类的静态方法或字段。静态构造器不允许带任何参数
class Employee
{
static Employee()
{
Random randomGenerator = new Random();
NextId = randomGenerator.Next(101, 999);
}
// ...
public static int NextId = 42;
// ...
}
4、静态属性
属性也能static。例如:
class Employee
{
// ...
public static int NextId
{
get
{
return _NextId;
}
private set
{
_NextId = value;
}
}
public static int _NextId = 42;
// ...
}
5、静态类
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) }");
}
}
6、封装数据
6.1 const 字段
-
const 字段是在编译时确定的值, 在运行时不会被改变。常量字段自动成为静态字段
-
如果一个程序集引用了另一个程序集中的常量,常量值将直接编译进引用的程序集中
class ConvertUnits
{
public const float CentimersPerInch = 2.54F;
public const int CupsPerGallon = 16;
}
6.2 readonly
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;
}
7、分部类
分部类是一个类的多个部分, 这些部分可以合并成一个完整的类。分部类主要用于将一个类的定义划分到多个文件中。 C# 使用关键字partial来声明分部类
// File: Program1.cs
partial class Program
{
}
// File: Program2.cs
partial class Program
{
}
8、分部方法
分部方法存在于分部类中,它允许在一个文件中声明方法,而在另一文件中实现该方法。例如:
// 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)
{
//...
}
}