Class
引用类型
Class inheritance
继承基类,除了构造函数的其他所有成员。在 C# 中,类只能直接继承单一基类;类可以实现一个或多个接口。
abstract 抽象类,可以拥有抽象方法(只定义,未实现功能)。抽象类不能被实例化
Struct
值类型
- 在 struct 中,字段(field)声明时不能初始化,除非将此栏位生命为常量(const),或者静态变量(static)
- struct 不能声明无参数的构造函数,和终接器(finalizer)
- struct 在分派时会被复制。struct 被分派给新的变量时,所有的数据都会被复制一份,并且所有对副本的修改,都不会影响到原本。
- struct 是值类型,class 是引用类型
- 不同于 class, struct 可以不通过使用 new 操作符,来实例化
- struct 可以声明有参数的构造函数
- struct 不能继承其他的 struct 和 class 并且也不能是其他类的基类。所有的 struct 都直接继承自 System.ValueType
- struct 可以实现接口
- struct 不能为 null 并且 struct 中变量的值不能被赋值为 null 除非该变量声明为可空类型
Class & Struct members
Fields
字段是 class 或者 struct 中直接声明的任何类型的变量
class 或者 struct 可能包含实例字段、静态字段。实例字段独立于类对象,改变各自对象的实例字段,不会影响到其他对象中的实例字段。相比之下,静态字段属于类自己,改变类对象的静态字段,会立即影响到其他类对象中的此静态字段。
通常,只将字段用于具有 private protected 访问属性的变量。
字段在构造函数执行之前会被立即初始化。
访问属性
public 可以被同一程序集中的任何代码,或者引用它的其他程序集访问
private 只可以被同一 class 或者 struct 的代码访问
protected 只能被同一 class 或者派生自该类的类的代码访问
internal 可以被同一程序集中的任何代码访问,不能被其他程序集中的代码访问
protected internal 可以被声明它的程序集中的任何代码访问,或者从另一程序集中的派生类中的代码访问
private protected 只可以在声明它的程序集中,被同一类或者派生自该类的代码访问
static field 可以在任何时间呼叫,甚至不通过类的实例,直接通过类呼叫。
readonly field 只能在初始化,或者构造函数中分派值。
static readonly field 类似于常量,不同的是 C# 编译器在编译阶段不能访问 static readonly field 只有在运行阶段可以访问。
Constants
常量在整个程式运行生命期不可改变值。只有 C# Build-In Type(除了 System.Object) 可以被声明为常量。
class Calendar1
{
public const int Months = 12;
}
class Calendar2
{
public const int Months = 12, Weeks = 52, Days = 365;
}
class Calendar3
{
public const int Months = 12;
public const int Weeks = 52;
public const int Days = 365;
public const double DaysPerWeek = (double) Days / (double) Weeks;
public const double DaysPerMonth = (double) Days / (double) Months;
}
int birthstones = Calendar.Months;
注意:若引用声明在其它代码中的常量(例如(DLLs)中),若该 DLL 声明了该常量的新值,该程式依旧持有该常量的旧值,直到重新编译该程式的版本。
Properties
属性(property)是一种成员,它提供灵活的机制来读取、写入、计算私有字段的值。
// Properties with backing fields
using System;
class TimePeriod
{
private double _seconds;
public double Hours
{
get { return _seconds / 3600; }
set {
if (value < 0 || value > 24)
throw new ArgumentOutOfRangeException(
$"{nameof(value)} must be between 0 and 24.");
_seconds = value * 3600;
}
}
}
class Program
{
static void Main()
{
TimePeriod t = new TimePeriod();
// The property assignment causes the 'set' accessor to be called.
t.Hours = 24;
// Retrieving the property causes the 'get' accessor to be called.
Console.WriteLine($"Time in hours: {t.Hours}");
}
}
// The example displays the following output:
// Time in hours: 24
// Expression body definitions
using System;
public class Person
{
private string _firstName;
private string _lastName;
public Person(string first, string last)
{
_firstName = first;
_lastName = last;
}
public string Name => $"{_firstName} {_lastName}";
}
public class Example
{
public static void Main()
{
var person = new Person("Isabelle", "Butts");
Console.WriteLine(person.Name);
}
}
// The example displays the following output:
// Isabelle Butts
// Auto-implemented properties
using System;
public class SaleItem
{
public string Name
{ get; set; }
public decimal Price
{ get; set; }
}
class Program
{
static void Main(string[] args)
{
var item = new SaleItem{ Name = "Shoes", Price = 19.95m };
Console.WriteLine($"{item.Name}: sells for {item.Price:C2}");
}
}
// The example displays output like the following:
// Shoes: sells for $19.95
Methods
方法(method)声明于 class 或者 struct 中,可以使用 public private 访问级别符修饰,可选修饰符为 abstract sealed
Method Parameters vs. Arguments
方法声明时,称为:parameter 方法调用时,称为:argument
Passing by Reference vs. Passing by Value
默认情况下,若将值类型传入方法中,方法得到的是该值类型对象的复制版本,因此对此参数的更改,不会影响到方法调用中的原始副本。可以通过使用 ref 关键字,通过引用方式传入值类型。
Async Methods - 异步方法
若使用 async 修饰方法,就可以在方法中使用 await 操作符,挂起方法的执行。异步方法的返回值类型可以为:Task<TResult> Task void
返回值为 void 的异步方法,主要用于事件处理程式的声明。异步方法不能定义 ref 或者 out 的方法变量
Expression Body Definitions
public Point Move(int dx, int dy) => new Point(x + dx, y + dy);
public void Print() => Console.WriteLine(First + " " + Last);
// Works with operators, properties, and indexers too.
public static Complex operator +(Complex a, Complex b) => a.Add(b);
public string Name => First + " " + Last;
public Customer this[long id] => store.LookupCustomer(id);
Constructors
当 class 或者 struct 被创建时,构造函数会被呼叫。若没有为 class 提供构造函数,C# 会默认创建一个无参的构造函数。
public class Person
{
private string last;
private string first;
public Person(string lastName, string firstName)
{
last = lastName;
first = firstName;
}
// Remaining implementation of Person class.
}
public class Location
{
private string locationName;
public Location(string name) => Name = name;
public string Name
{
get => locationName;
set => locationName = value;
}
}
Static constructors - 静态构造函数
class 或者 struct 可以拥有静态构造函数,其用来初始化此种类型中的静态成员。静态构造函数必须声明为无参数的。
public class Child : Person
{
private static int maximumAge;
public Child(string lastName, string firstName) : base(lastName, firstName)
{ }
static Child() => maximumAge = 18;
// Remaining implementation of Child class.
}
Events
事件使 class 或者对象,在发生某些情况时可以通知其他 class 或者对象。
Finalizers
终接器(又称为 destructors 析构函数),用于在对象被垃圾回收器回收时,执行最终清理工作。
- Finalizers 不能声明在 struct,只能用在 class 中
- class 只能拥有一个 finalizer
- Finalizers 不能被继承或者重载
- Finalizers 不能被显示呼叫执行,它自动执行
- Finalizers 不需要修饰符或者参数
Indexers
索引器允许 class 或者 struct 的实例像数组一样被索引。
using System;
class SampleCollection<T>
{
// Declare an array to store the data elements.
private T[] arr = new T[100];
// Define the indexer to allow client code to use [] notation.
public T this[int i]
{
get { return arr[i]; }
set { arr[i] = value; }
}
}
class Program
{
static void Main()
{
var stringCollection = new SampleCollection<string>();
stringCollection[0] = "Hello, World";
Console.WriteLine(stringCollection[0]);
}
}
// The example displays the following output:
// Hello, World.
// Expression Body Definitions
using System;
namespace ASP_NET_Roadmap.Classes_and_Structs
{
public class SampleCollection1<T>
{
// Declare an array to store the data elements.
private T[] arr = new T[100];
int nextIndex = 0;
// Define the indexer to allow client code to use [] notation.
// C# 6.0 支持
public T this[int i] => arr[i];
public void Add(T value)
{
if (nextIndex > arr.Length)
{
throw new IndexOutOfRangeException($"The collection can hold only {arr.Length} elements.");
}
arr[nextIndex++] = value;
}
}
}
using System;
namespace ASP_NET_Roadmap.Classes_and_Structs
{
public class SampleCollection2<T>
{
// Declare an array to store the data elements.
private T[] arr = new T[100];
// Define the indexer to allow client code to use [] notation.
// C# 7.0 支持
public T this[int i]
{
get => arr[i];
set => arr[i] = value;
}
}
}
- 索引器使对象能以类似数组的方式被索引
- get 访问器返回值,set 访问器设置值
- this 关键字,用于定义索引器
- value 关键之,用于定义被 set 索引器分配的值
- 索引器不必用整数值索引,用户可自定义查找的机制
- 索引器不能被重载
Nested Types
声明在 class 或者 struct 中的类型,称为嵌套类型。嵌套类型的默认访问属性为:private
class Container
{
class Nested
{
Nested() { }
}
}
class Container
{
public class Nested
{
Nested() { }
}
}
public class Container
{
public class Nested
{
private Container parent;
public Nested()
{
}
public Nested(Container parent)
{
this.parent = parent;
}
}
}
Container.Nested nest = new Container.Nested();
嵌套类型的 class 的访问属性可为:public, protected, internal, protected internal, private, private protected
在 sealed class(封闭类)中声明 protected, protected internal, private protected 的嵌套类,会引起 CS0628 编译警告
嵌套类型的 struct 的访问属性可为:public, internal, private