Class

Tags

字段和属性

字段:普通的成员变量
属性:后台field + getter + setter,属于一种语法糖,在编译时自动变成俩方法
setter中传入的值的名称为value
这个value在作为异常类构造函数的参数paramName 时,使用nameof(value) 而非 value(C# 6.0)(why?)
用于封装字段
private int _Age; public int Age { get => _Age; set{ if(value > 100){ Console.WriteLine("Invalid age"); }else { _Age = value; } } } public string? Salary { get; set; } = "Not Enough"; // C# 6.0
简单写法
public int Age {get; set;}
 
private 修饰则只能在类内读写
 

indexer

class Student { private Dictionary<string, int?> gradeDic = new Dictionary<string, int?>(); public int? this[string index] { get { /* return the specified index here */ if (gradeDic.TryGetValue(index, out var grade)) { return grade; } else { return null; } } set { /* set the specified index to value here */ gradeDic[index] = value; } } }
Student stu = new Student { ["Math"] = 100 }; var grade = stu["Math"]; Console.WriteLine(grade); //100 Console.WriteLine(stu["M"].HasValue); //false
 
 

继承与多态

虚方法:可被子类重写(使用overwrite关键字)的方法
 
 

构造函数链:this调用另一个构造函数

在执行本构造函数之前,先调用另一个构造函数
public class Employee { public Employee(string firstName, string lastName) { this.firstName = firstName; this.lastName = lastName; } public Employee(string firstName, string lastName, int id) : this(firstName, lastName) { Id = id; } }
 
 

可读写的不可空引用型属性

public class Employee { public Employee(string name) { this.Name = name } private string? _Name; // 实质上不可空,只是为了避免编译器的警告 public string Name { get => _Name!; // 表示这一字段不会返回null set => _Name = value ?? throw new ArgumentNullException(nameof(value)); // 赋值时不接受null值 } }
 

只读的不可空引用型属性

public Employee (string name) { Name = name ?? throw new ArgumentNullException(nameof(name)); } public string Name { get; }
 
 

解构函数

声明Deconstruct 函数,将类的字段拆出来
签名要求:返回值void,接收两个或多个out parameters
public class Employee { public void Deconstruct (out int id, out string name) { (id, name) = (Id, Name); // 需要C# 6.0后的Tuple语法 } // Definition of Id, Name } class Program { static void Main() { // ... (_, curName) = employee; } }
💡
注意:只能对几个变量解构赋值,不允许对一个 元组变量 进行赋值
(int, string) tupleVar = employee; // ❌ (int id, string name) tupleVar2 = employee; // ❌
 
 

静态构造函数

运行时在类被初次访问时调用
可用于初始化类的static字段
💡
静态数据的 声明时初始化 V.S. 静态构造函数
若同时在静态构造函数和声明时初始化静态数据,则由CIL代码可见:
声明时的赋值指令被移动到了静态构造函数的开头,最终保留的是 在静态构造函数中初始化的值
Best Practice: 在声明时静态初始化,因为对静态变量的初始化代价较高,在访问某静态数据前没有必要将其初始化。
 
 

静态类

若一个类只存在静态字段或方法,则可以声明为静态的
防止coder去声明实例 或 加入实例字段、方法
编译器会自动在CIL中将其标记为abstract 和 sealed,类会被指定为不可扩展,即不能派生其他类
using static <StaticClassName>; 可以直接调用其中的静态方法
 

扩展方法:自定义的对某些类的扩展方法

C# 3.0后
该方法属于一个static类,并且自身是 public static 的(一般会把对一个类SomeType的扩展方法全部放在一个SomeTypeExtention类中)
第一个参数用 this 修饰,即需要扩展的类的实例(操作对象也是这个参数)
主要应用:LINQ (System.Linq)
static class DoubleExtention { public static double Round(this double input, int digits){ double res = Math.Round(input, digits); return res; } } // double类型本无Round方法,经过上面这波就有了 double x = 3.14159; double xr = x.Round(4); // xr: 3.1416
💡
通过继承原类型中的方法会更优于扩展方法,当扩展方法与原类型中的方法同名会覆盖扩展方法
 

readonly V.S. const

 

嵌套类

仅当 类A只服务类B时才用
可以(or不这么做设计就会很烂)指定为private
嵌套类可访问包容类(它包含于的那个类)的任意变量,但反之不行
 
 

分部类

范围:同一程序集里的所有文件
最后一起生成类的目标代码
分部方法:仅存在于分部类中,不允许返回任何值(return void, 不可使用out参数,但是可以使用ref参数),允许在一个类里声明 另一个类里定义
分布类的存在意义:适用于生成代码的场景(如xLua框架中,生成[CsharpCallLua]的部分类代码)