3 栈帧 递归 类成员 静态字段 常量 静态函数 属性 构造函数 析构函数 this readonly 索引器 分部方法 分部类

好记性不如烂笔头

栈帧

​ 现在,我们已经知道了 本地变量(局部变量),参数都存放在栈上,现在我们研究下其组织。

​ 在调用方法的时候,内存从栈的顶部开始分配,保存和方法关联的一些数据项。这块内存叫做方法的栈帧。

  • 栈帧包含的内存保存如下内容
    • 返回地址,也就是在方法退出的时候继续执行的位置。
    • 这些参数分配的内存,也就是方法的值参数,或者还可能是参数数组(如果有的话);
    • 各种和方法调用相关的其他管理数据项
  • 在方法调用是,整个栈帧会压入栈。
  • 在方法退出的时候,整个栈帧都会从栈上弹出。弹出的栈帧有的时候也叫做栈展开(unwind);
static void MethodA(int a,int b)
{
    Console.WriteLine("Enter MethodA: {0} {1}",a,b);
    MethodB(12,10);
    Console.WriteLine("End MethodA");
}

static void MethodB(int a,int b)
{
    Console.WriteLine("Enter MethodB: {0} {1}",a,b);
    Console.WriteLine("End MethodB");
}
static void Main(string[] args)
{
    MethodA(12, 24);
    Console.ReadKey();
}

输出如下:

Enter MethodA: 12 24
Enter MethodB: 12 10
End MethodB
End MethodA

下图演示了调用方法是栈帧压入栈 和 弹出的过程。

image-20221002004937366

递归

除了调用其他方法,方法也可以调用自身。这叫递归

递归必须有结束条件,不然就一直递归下去了

public static int 递归方法(int value)
{

    if (value <= 1)
    {
        return value;
    }
    return value * 递归方法(value-1);

}

递归方法(5);  // 120
5 * (4*(3*(2))) = 120;

调用方法自身的机制和调用其他方法都是完全一样的,都是每一次调用方法把新的栈帧压入栈顶

public static void 递归方法2(int value)
{
    if (value == 0) return;
    递归方法2(value - 1);
    Console.WriteLine("{0}",value);
}

输出:

1
2
3
4
5

原理:

image-20221002011324930

=深入了解类==

1 类成员

数据成员 函数成员(执行代码)
字段 方法
常量 属性
构造函数
析构函数
运算符
索引
事件

2 成员修饰符的顺序

​ 学习过程中,我们看到字段和方法的声明可以包括 public private 这样的修饰符。多个修饰符可以在一起使用,自然产生了一个问题:他们需要按什么顺序排列?

​ 类成员声明语句由下列部分组成: 核心声明,一组可选的修饰符 和 一组可选的特性(attribute)。用于描述这个结构的语句如下。方括号表示可以选择的。

[特性] [修饰符] 核心声明
  • 修饰符
    • 如果有多修饰符,必须放在核心声明之前。
    • 如果有多个修饰符,可以是任意顺序的
  • 特性
    • 如果有特性,必须放在修饰符前和核心声明前
    • 如果有多个特性,可以是任意顺序的

image-20221002015244391

3 静态字段

静态字段被类的所有实例共享,所有的实例都访问同一块内存位置。因此,如果改内存位置的值被一个实例改变,这种改变对所有实例都可见。

可以用static修饰符将字段声明为静态,如

class D
{
    int Number; // 实例字段
    static int Number2; // 静态字段
}

image-20221002015636142

4 从类的外部访问静态成员

类名.静态字段 = 5;   // 访问静态类成员

4.1 静态成员的生存期

  • 之前我们已经看到了,只有在实例创建之后才能产生实例成员,在实例销毁之后实例成员也就不在了
  • 但是即使类没有实例,也存在静态成员,并且可以访问

5 静态函数成员

  • 同静态字段一样,独立于任何类的实例,没有实例也可以调用
  • 静态函数不能访问实例成员,但是能访问其他静态成员
class A
{
    public static void Func()
    {
        // ...
	}
}

调用方法于静态成员一样

6 其他静态成员类型

数据成员 函数成员
字段 方法
类型 属性
构造函数
运算符
事件

7 成员常量

只能在类中声明而不是方法

class MyClass
{
    const int IntVal = 100;  // 无法赋值,必须初始化
}

8 常量与静态量

成员产品比本地常量更有趣,因为他们表现得和像静态值。他们对每个实例都是可见的,没有实例也可以使用。与真正的静态量不同,常量没有自己的存储位置,而是在编译阶段被编译器做了替换

class A
{
 	public const double PI = 3.14159;
}

class Program
{
    static void Main()
    {
        A.PI; // 3.14159
	}
}
static const double PI = 3.14159; // 错误,不能将常量声明为static

9 属性

与字段类似,属性有如下特征

  • 他有类型
  • 他可以被赋值和读取
    • 然而他和字段不同,属性1是一个函数成员
  • 它不为数据存储分配内存!
  • 它执行代码
    • 属性是指的一组有两个匹配的,称为访问器的方法。
  • set访问器为属性的赋值
  • get访问器为属性的取值
int MyValue
{
    set
	{
    	// ..    
    }
    
    get
    {
        // ..
    }
}

9.1 属性声明和访问器

  • set访问器总是:
    • 拥有一个单独的,隐示的值参,名称为value,与属性的类型相同;
    • 拥有一个返回类型void
  • get访问器总是:
    • 没有值参
    • 拥有一个属性类型一样的返回类型

image-20221002022332975

9.2 只读和只写属性

  • 只有get 是只读 无法写入
  • 只有set 是只写 无法读取

9.3 静态属性

  • 不能访问类的实例
  • 不管类是否有实例,他们都是存在的
  • 当从类的外部访问,必须使用类名引用,而不是实例名

10 实例构造函数

  • 构造函数用于初始化类实例的状态
  • 如果希望能从类的外部创建类的实例,需要将构造函数声明为public
  • 构造函数名与类名一样
  • 构造函数不能有返回值
  • 构造函数可以重载
class A{
    public void A(){
		// ... 
    }
}

10.1 默认构造函数

​ 如果在类的声明中没有显示的声明构造函数,编译器会提供一个隐式的构造函数,他有以下特征:

  • 没有参数

  • 方法体为空

    如果你声明了构造函数,那么编译器就不会设置默认的构造函数

10.2 静态构造函数

​ 构造函数也可以声明为static。通常,静态构造函数用于初始化类的静态字段

  • 初始化类级别的项
    • 在引用任何静态成员之前
    • 在创建累的任何实例之前
  • 类只能有一个静态函数,而且不能带参数。
  • 静态构造函数不能有访问修饰符

11 析构函数

执行在类的实例被销毁之前需要的清理或释放非托管资源的行为

非托管资源 pass....

12 readonly修饰符

​ 字段可以用readonly修饰符声明。其作用类似于将字段声明为const

  • const字段只能在字段的声明语句中初始化,而readonly字段可以在下列任意为设置他的值
    • 字段声明语言,类似const
  • readonly字段的值可以在运行时决定、

13 this关键字

​ this关键字在类中使用,是对当前实例的引用。他只能用在下列类成员的代码块中。

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

14 索引器

14.1 什么是索引器

image-20221002230237473

14.2 声明索引器

  • 索引器没有名称。在名称的位置是关键字this。
  • 参数列表在方括号中间。
  • 参数列表中必须至少声明一个参数

image-20221002230405397

image-20221002230412841

14.3 索引器的set访问器

  • 一个隐示参数,名称为value,value持有要保存的数据;
  • 一个或跟多索引参数,表示数据应该保存到哪里。
  • 他的返回值类型为void

image-20221002230650503

14.4 索引器的get访问器

  • 他的参数列表和索引器的声明中的相同
  • 他的返回值类型与索引器相同类型

image-20221002230813976

索引器可以重载 多个参数

image-20221002230930451

15 访问器的访问修饰符

image-20221002231323351

image-20221002231335212

16 分布类 和分布类型

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

  • 每个分部类的声明都含有一些类成员的声明
  • 累的分部类声明可以在同一文件中也可以在不同文件中

每个局部声明必须标为 partial class, 而不是单独的关键字class。分部类声明扛起来和普通类相同,除了附加的类型修饰符 partial

image-20221002231733981

类型修饰符partial不是关键字,所以在器他上下文中,可以在程序把他用作标识符。但是之间用在关键字class,struct或interface之前时,他表示分部类型。

17 分部方法

  • 定义分部方法声明
    • 给出签名和返回值类型
    • 声明的实现部分只是一个分号。
  • 实现分部方法声明
    • 给出标签名和返回值类型
    • 正常书写代码块语句
  • 定义声明和实现声明的签名和返回类型必须匹配。签名和返回类型有如下特征:
    • 返回值必须是void
    • 签名不能包括访问修饰符,这使分部方法是隐示私有的
    • 参数列表补鞥呢包含out参数。

image-20221002232616388

posted @ 2022-10-29 00:03  LD_Dragon_sky  阅读(29)  评论(0编辑  收藏  举报