C#笔记(类)

1 类的基本概念

类是引用类型,类的类成员类型有9种,分别是:

  • 数据成员:
    • 字段
    • 常量
  • 函数成员:
    • 方法
    • 属性
    • 构造函数
    • 析构函数
    • 索引
    • 事件
    • 运算符

字段和方法是最重要的类成员类型

C#在类型的外部不能声明全局变量(也就是变量或字段),所有字段都属于类型,而且必须在类型声明内部声明。

C#在类型的外部不能声明全局函数(也就是函数或方法),所有方法都属于类型,而且必须在类型声明内部声明。而且所有方法声明必须包含返回类型或 void

2 成员修饰符

修饰符是 成员 声明的可选部分,可以分为 访问修饰符静态修饰符常量修饰符等,如果有多个修饰符,可以是任何顺序排列。

2.1 访问修饰符

访问修饰符是成员声明的可选部分,指明程序的其他部分如何访问成员,五种成员访问控制如下:

  • private:私有的,默认的访问类型
  • public:公有的
  • protected:派生继承的类可以访问
  • internal:同一程序集中的类可以访问
  • protected internal:同一程序集中的派生继承类可以访问

2.2 静态修饰符 static

类成员可以关联到类的一个实例,也可以关联到类的整体,即所有类的实例。
默认情况下,每个实例都拥有自己的各个类成员副本,这些成员称为 实例成员(如实例方法或实例字段),而关联到类的整体,即所有类的实例,称为 静态成员,使用 static 修饰符表示。
静态字段与实例字段的区别:

  • 静态字段被类的所有实例共享,即所有实例都访问同一内存位置
  • 生命周期不同,即使类没有实例,也存在静态成员,并可以访问(同理静态函数也一样),外部访问时,必须使用类名,而不是实例名
  • 静态函数成员不能访问实例成员,可以访问其他静态成员
  • 成员常量 不能声明为 static,注意 成员常量并不同于本地常量(后者在方法内部初始化)

常量成员索引器成员 类型不能声明 static

2.3 成员常量 const

成员变量类似于本地变量,只是它们被声明的位置不同,同时,成员常量表现得像 静态值,即没有类的实例也可以使用,与真正的静态量不同的是,常量没有自己的存储位置,而是在编译时被编译器替换,并且一旦值被设定就不能改变,以及在必须在声明的同时赋值

2.4 readonly 修饰符

const 相比较,readonly 修饰符有以下不同:

  • const 字段只能在字段的声明语句中初始化,而 readonly 字段可以在以下状况位置中设置数值
    • 字段声明语句
    • 类的任意构造函数(静态成员需在静态构造函数中初始化)
  • const 的行为总是静态的,而 readonly 则可以是实例字段,也可以是静态字段,并且它在内存中有存储位置

3 方法

3.1 关键字

3.1.1 var 关键字

var 关键字是句法的速记,表示如何可以从初始化语句的右边推断出的类型,其中有几个注意事项:

  • 只能用于本地变量,不能用于字段
  • 只能在变量声明中包含初始化时使用
  • 一旦编译器推断出变量的类型后,它就是固定且不能更改

3.1.2 const 关键字

本地常量(注意不是本地变量),一旦被初始化就不能更改,且声明时必须初始化,在声明类型之前添加关键字 const

3.2 参数

3.2.1 形参

形参是本地参数,它声明在方法的参数列表中,而不是方法体中并在方法开始之前初始化(除输出参数外)

3.2.2 实参

当代码调用一个方法时,形参的值必须在方法的代码开始执行之前被初始化,用于初始化形参的表达式或变量称作实参。
实参位于方法调用的参数列表中

根据各自以略微不同的方式从方法传入或传出数据(类似类型中的值类型和引用类型),参数分为几种

3.2.3 值参数

值参数,通过将实参的值复制到形参的方式把实参的值,方法被调用时,系统做如下操作:

  • 在栈中为形参分配空间
  • 将实参的值复制给形参

3.2.4 引用参数

在使用引用参数时,注意事项:

  • 必须在方法的声明调用中都使用 ref 修饰符
  • 传入的实参必须是变量,并且在调用方法前需明显被赋值

相对于值参数,系统在栈上为形参分配内存,相反,引用参数具有以下特征:

  • 不会为形参在栈上分配内存
  • 实际上形参的参数名将作为实参变量的别名

3.2.5 输出参数

输出参数的作用与引用参数的作用类似,区别在于引用参数必须传入已初始化的实参,而输出参数则是可以在内部初始化实参,而且是一定要初始化,并要在使用前初始化。

  • 必须在方法的声明调用中都使用 out 修饰符
  • 传入实参必须是变量,在方法体内部初始化且一定要在读取前初始化,这意味着该参数的初始值无关的

3.2.6 参数数组

  • 参数数组表示的所有参数都是必须具有相同的类型
  • 参数数组必须是参数列表中的最后一个
  • 参数数组中的所有参数都必须具有相同的类型
  • 在数据类型前使用 params 修饰符,在数据类型后放置一组空的方括号 [],如:
void ListInts(params int[] invals)
{
    ...
}

3.3 方法重载

一个类中可以有一个以上的方法拥有相同的名称,这叫做方法重载,使用相同名称的每个方法必须有一个和其他方法不相同的签名,签名有下列信息组成:

  • 方法的名称
  • 参数的数目
  • 参数的数据类型和顺序
  • 参数的修饰符
  • 不包括返回类型

3.4 命名参数

语法:形参的名字后面跟着 冒号 和实际的参数值或表达式

3.5 可选参数

语法:使用形参名字后跟 等号默认值

4 属性

属性 是代表类的实例或类中的一个数据项的成员,写入与读取都像 字段,就类似Python中的 settergetter属性本身没有任何存储,访问器决定任何处理发进来的数据,以及什么数据应被发送出去
因此属性与字段的不同,属性是一个函数成员,属性是指定的一组两个匹配的、称为 访问器方法

  • set 访问器是为属性赋值的方法
  • get 访问器是从属性取值的方法
int MyValue
{
    set
    {
        SetAccessorCode
    }
    get
    {
        GetAccessorCode
    }
}

4.1 set 访问器

set 访问器可以想象成一个方法,拥有一个单独的、隐式的值参,名为value,与属性类型相同,拥有一个返回类型 void
正如Python中的 settergetter,属性主要起一个过滤的作用,所以属性常与字段关联,常见的方式是:在类中将字段说明为 private 以起到 封装该字段,并声明一个 public 属性来控制从类的外部对该字段的访问。该字段常称为 后备字段后备存储

属性和后备字段有两种命名约定:

  • 一般属性命名使用 Pascal 命名,而字段使用 Camel 命名
  • 一般属性命名使用 Pascal 命名,而字段使用 下划线 + Camel 命名

eg:

class C1
{
    private int _theRealValue;
    public int TheRealValue
    {
        set
        {
        _theRealValue = value;  //隐式值参
        }
        get 
        {
        return _theRealValue; //get访问器没有参数,拥有一个与属性类型相同的返回类型
        }
    }
}

4.2 只读和只写属性

要想不定义属性的某个访问器,可以忽略该访问器的声明

  • 只有 get 访问器的属性为 只读属性
  • 只有 set 访问器的属性为 只写属性

4.3 自动实现属性

属性常被关联到 后备字段, C#提供了 自动实现属性允许只声明属性而不声明后备字段,编译器会为你创建隐藏的后备字段,创建自动实现属性 不能提供访问器的方法体,它们必须被简单地声明为 ; 分号。

class C1
{
 public int MyValue
    {
        set;get;
    }
}

4.4 静态属性

与其它静态成员一样,静态属性的访问器具有以下特点:

  • 不能访问类的实例成员,但能被实例成员访问
  • 不管类是否有实例,它们都是存在的
  • 当从类的外部访问时,必须使用类名,而不是实例名

4.5 访问器的访问修饰符

可以私自为访问器设置独自的访问修饰符,不过访问器修饰符有以下限制:

  • 仅当成员(索引器或属性)同时有 get 访问器和 set 访问器是,才能设置访问修饰符
  • 两个访问器只能有一个设置修饰符
  • 访问器的访问修饰符必须比成员的访问修饰符低级

5 索引器

索引器可以使访问字段如同数组一样。
索引器如同属性一样,是一组 getset 访问器,索引器与属性都是相似的,如:

  • 和属性一样,索引器不用分配内存来存储
  • 索引器和属性都主要被用来访问其他数据成员
  • 可以只有一个访问器,也可以两个都有
  • 不必一定关联某个字段或属性,只需 get 访问器返回某个指定类型的值就可以了

但是,索引器总是实例成员,故不能声明 static

5.1 声明索引器

语法:

  • 索引器没有名称,转而使用关键词 this
  • 参数列表在 方括号
  • 参数列表中至少声明一个参数
ReturnType this [Type param1,...]
{
    get
    {
        SetAccessorCode
    }
    set
    {
        GetAccessorCode
    }
}

5.2 索引器重载

只要索引器的参数列表不同,类就可以有多个索引器,这就叫做索引器重载

6 实例构造函数

与属性类似,实例构造函数(构造器)是一个 方法,用于创建类的每个实例是执行,如果在类的声明中没有显式的提供构造函数,那么编译器会提供一个隐式的默认构造函数,要点:

  • 如果希望能从类的外部创建类的实例,需要将构造函数声明添加 public 修饰符
  • 构造函数的 名称和类名相同
  • 构造函数不能有返回值

6.1 带参数的构造函数

构造函数可以带参数,也可以被重载

6.2 静态构造函数

构造函数可以声明为 static,实例构造函数初始化类的每个新实例,而 static 构造函数初始化 类级别的项(即如静态字段),要点:

  • 静态构造函数声明中使用 static 修饰符
  • 类只能有一个静态构造函数,且不能带参数
  • 不能有 访问修饰符
  • 静态构造函数不能访问所在类的实例成员,因此不能使用 this 访问器

6.3 对象初始化语句

对象初始化语句 扩展了创建语法,在表达式的尾部放置了一组成员初始化语句,这允许你在创建新的对象实例时,设置 字段属性 的值,语法:

new TypeName(ArgList) {FieldOrProp = InitExpr, FieldOrProp = InitExpr...}; //使用花括号

要点:

  • 创建对象的代码必须能够访问初始化时重设置的字段和属性
  • 初始化发生在构造方法执行之后

7 this 关键字

this 关键字是对当前实例的引用,它只能被用在 类成员的代码块 中,如:

  • 实例构造函数
  • 实例方法
  • 属性和索引器的实例访问器

静态成员不是实例的一部分,所以与之相关的都不能使用 this

8 分布类和分布类型

类的声明可以分割成几个分部类的声明:

  • 每个分部类的声明都含有一些类成员的声明
  • 类的分部类声明可以在同一个文件中也可以在不同的文件中
  • 声明时,添加 partical 修饰符

eg:

partical class MyPartClass
{
    ...
}

9 析构函数

析构函数(destructor)执行在类实例被销毁之前需要的清理或释放非托管资源的行为

10 继承

继承是使用一个已经存在的类作为新类的基础进行扩展,这样的已存在的类称为 基类,新类称为 派生类
声明一个派生类,要在声明的类名后添加 基类规格说明,基类规格说明由 冒号基类名称 组成,eg:

class SonClass : ParentClass
{
    ...
}

要点:

  • 派生类扩展它的基类,派生类不能删除它所继承的任何成员
  • 所有类都派生自 object 类,所有的 基类规格说明 中只能有一个单独的类

10.1 屏蔽基类的成员

虽然派生类不能删除它的继承的任何成员,但可以用重构一个成员从而 屏蔽 覆盖掉基类成员。创建屏蔽成员的要点:

  • 声明时 需要使用 new 修饰符
  • 数据成员:声明一个新的相同类型、相同名称的数据成员
  • 函数成员:声明 相同签名 的函数成员(签名由名称和参数列表组成,不包括返回类型
  • 也可以 屏蔽静态成员

10.2 基类的访问

10.2.1 在派生类中基类的访问

如果派生类想要完全访问被隐藏的继承成员,可以使用 基类访问表达式(注:是派生类使用,而不是实例使用)。
基类访问表达式 由关键字 base 后跟 . 和成员类名组成,eg:

Console.WriteLine("{0}", base.Field1);

10.2.1 在实例层面中基类的访问

使用 base 只能在派生类中使用,如果想要在实例的层面上完全访问被隐藏的继承成员,就需要使用到 类型转换运算符
即如果有一个派生类的实例,就可以获取 该实例基类部分 的引用(使用 类型转换运算符 把该实例转换为 基类类型
语法:类型转换运算符 放置在派生类的实例前面(类型转换运算符由 括号 括起要被转换成类名),eg:

MyDerivedClass derived = new MyDerivedClass();
MyBaseClass mybc = (MyBaseClass) derived;

10.3 虚方法、覆写方法以及抽象成员

基类的方法使用 virtual 修饰符,称为 虚方法,派生类的方法使用 override 修饰符,称为覆写方法
虚方法 可以使基类的引用“升至”派生类内,即尽管使用了基类访问,返回的还是派生类的方法、数据。使用虚方法,需要满足下面条件:

  • 派生类的方法和基类的方法有 相同的签名返回类型
  • 覆写和被覆写的方法必须有相同的可访问性
  • 不能覆写 static 方法或 非虚方法
  • 只有方法属性索引器事件可以被声明为 virtualoverride

10.4 抽象类、密封类与静态类

10.4.1 抽象成员

抽象成员与虚方法中的虚成员类似,都是指设计为被覆写的函数成员,抽象成员有以下特征:

  • 必须是一个函数成员
  • 必须使用 ahstract 修饰符,并必须在派生类中指定 override 修饰符
  • 不能有实现代码块,使用 ; 分号代替
    eg:
// 函数方法
abstact public void PrintStuff(string s);

// 属性
abstract public int MyProperty
{
    get;
    set;
}

10.4.2 抽象类

抽象类就是指设计为被继承的类,抽象类只能被用作其他类的基类,因此不能创建抽象类的实例,同样的抽象类使用 abstract 修饰符声明,要点:

  • 抽象类可以 包含 抽象成员或普通非抽象成员
  • 抽象类可以派生自另一个抽象类
  • 任何派生自抽象类的类 必须使用 override 关键字 实现该类所有的抽象成员

10.4.3 密封类

相对于抽象类必须作为基类,密封类则不能被继承,密封类使用 sealed 修饰符标注

10.4.4 静态类

静态类中所有的成员都是静态的,静态类用于存放不收实例数据影响的数据和函数,主要用途是创建一个包含一组数学方法和值的数据库,要点:

  • 类需添加 static 修饰符
  • 所有成员都是静态的
  • 可以有静态构造函数,但不能有实例构造函数,不能创建实例
  • 静态类不能继承

10.5 派生类的构造函数初始化语句

有两种形式的构造函数初始化语句,使用关键字 basethis

10.5.1 关键字 base

默认情况下,在构造实例时,将调用基类的无参数构造函数,如果派生类希望使用一个指定的基类构造函数,就 必须在 构造函数初始化语句 中指定它
构造函数初始化语句模板后跟 : 冒号加关键字 base 和要调用的基类构造函数的参数列表组成。
在基类参数列表中的参数必须在 类型和顺序方面 与调用的基类构造函数的列表参数相匹配,eg:

public MyDerivedClass(int x, string s):base(s,x)

10.5.2 关键字 this

base 调用基类的构造函数不同, this 关键字用于调用本类内其他的构造函数,起组合的作用。语法与 base 的一样。
构造函数初始化语句模板后跟 : 冒号加关键字 this 和要调用的构造函数的参数列表组成。eg:

class MyClass{
    readonly int firstVar;
    public string UserName;
    private MyClass()
    {
        firstVar = 20;
    }
    public MyClass(string fristName):this()
    {
        UserName = fristName;
    }
}

10.6 扩展方法

想要在一个类中扩展方法,可以采取以下方式:

  • 如果有源代码并可以直接修改这个类
  • 没有源代码,但该类不是 密封 的,可以把其用作成一个基类,在派生类中实现这个方法
  • 如果该类是 密封的,这样就不得不在另一个类中使用该类的 公有可用成员 编写一个方法

第三种方式例子:

class MyData
{
    private double D1;
    private double D2;
    private double D3;
    public MyData(double d1, double d2, double d3)
    {
        D1 = d1;
        D2 = d2;
        D3 = d3;
    }
    public double Sum()
    {
        return D1 + D2 + D3;
    }
}

static class ExtendMyData
{
    public static double Average(MyData md)
    {
        return md.sum()/3;
    }
}
class Program
{
    static void Main()
    {
        MyData md = new MyData(3, 4, 5);
        Console.WriteLine("Average:{0}", ExtendMyData.Average(md));// ExtendMyData 是静态类
    }
}

但如果想要直接通过调用基类的实例自身该添加功能的形式,就需要用到 扩展方法 了,而这只需在第三种方式中做小小的改动,就是在创建新功能的 参数说声明中的类型名前添加关键字 this,这 其中的要求:

  • 声明扩展方法的类必须是静态类 static
  • 扩展方法本身必须声明为 static
  • 扩展方法必须包含关键字 this 作为他的第一个参数类型,并在后面跟着它所扩展的类的名称

10.7 命名约定

  • Pascal 命名用于类型名称和类中对外可见成员的名称,如:命名空间、类、方法、属性、公共字段等。
  • Camel 命名:用于 局部变量的名称 和方法声明的 形参名称
  • 下划线加 Camel 命名:用于私有和受保护的 字段
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值