<b>访问修饰符</b>
字段
访问修饰符 类型 标识符;
方法
访问修饰符 返回类型 方法名()
{
、、、
}
<b>五种成员访问控制如下</b>
私有的(private) 私有成员只能从声明它的类的内部访问,其他的类不能看见或访问它们。
公有的(public)
受保护的(protected)
内部的(internal)
受保护内部的(protected internal)
<b>实例成员</b>:类的每个实例都是不同的实体,它们有自己的一组数据成员,不同于同一类的其他实例,因为这些数据成员都和类的实例相关,所以被称为实例成员。
<b>静态成员</b>:实例成员是默认类型,但也可以声明与类而不是实例相关的成员,它们被称为静态成员。
<b>方法</b>
本质上,方法是一块具有名称的代码,可以使用方法的名称执行代码,也可以把数据传入方法并接收数据输出。
方法是类的函数成员,方法有两个主要部分,方法头和方法体
方法头指定方法的特征,包括:
方法是否返回数据,如果返回返回什么类型;
方法的名称;
什么类型的输入可以传入方法。
方法体包含了可执行代码的语句序列。执行从方法体的第一条语句开始,一直到方法结束.
<b>常量</b>
两个特征:声明中必须初始化;声明后不能改变。
常量声明的语法与变量或字段的声明相同,除了下面内容:
在类型之前增加关键字const,关键字const不是一个修饰符,而是核心声明的一部分,它必须直接放在类型的前面。;
必须有初始化语句。
<b>引用参数</b>
使用引用参数是,必须在方法的声明和调用中都使用ref修饰符;
实参必须是变量,在用作实参前必须被赋值。如果是引用类型变量,可以付志伟一个引用或NULL值。
void MyMethod(ref int val) //方法声明
{...}
int y=1; //实参变量
MyMethod(ref y); //方法调用
MyMethod(ref 2+3) //出错,val必须使用变量
<b>输出参数</b>
输出参数用于从方法体内把数据传出到调用代码,它们非常类似引用参数。它有如下要求:
<li>必须在声明的调用中都使用修饰符,输出参数的修饰符是out而不是ref</li>
<li>实参必须是变量,不能是其他表达式类型。</li>
<b>参数数组</b>
重点:
<li>在一个参数列表中只能有一个参数数组。</li>
<li>如果有,他必须是列表中的最后一个</li>
声明一个参数数组必须要做的事:
<li>在数据类型前使用params修饰符</li>
<li>在数据类型后放置一组空的方括号</li>
void ListInts(params int[] inVals)
{....}
<li>数组是一组整齐的相同类型的数据项。</li>
<li>数组使用一个数字索引进行访问。</li>
<li>数组是一个引用类型,因此它的所有数据项都保存在堆中</li>
ListInts(10,20,30): //所有元素必须是方法声明中指定的类型
int[] intArray={1,2,3};
ListInts(intArray); //一个数组变量
<li>在声明中需要修饰符</li>
<li>在调用中不允许有修饰符</li>
参数类型 | 修饰符 | 是否在声明时使用 | 是否在调用时使用 | 执行 |
---|---|---|---|---|
值 | 无 | 系统把实参的值复制到形参 | ||
引用 | ref | 是 | 是 | 形参是实参的别名 |
输出 | out | 是 | 是 | 形参是实参的别名 |
数组 | params | 是 | 否 | 允许传递可变数目的实参到方法 |
<b>栈帧</b>
当一个方法被调用时,在栈顶分配了一块内存用于保存一定数量与方法相关的数据项,这块内存叫方法的栈帧。
<ul><li>栈帧含有保存下列内容的内存:</li>
<ol><li>返回地址——就是方法退出时在哪儿继续执行</li>
<li>分配内存的参数——就是方法的值参数,如果有参数数组</li>
<li>与方法调用相关的其他各种管理数据项</li></ol>
<li>方法被调用时,它的整个栈帧被压入栈中。</li>
<li>方法退出时,它的整个栈帧被从栈中弹出。弹出一个栈帧有事也称为释放栈</li>
class Program
{
static void MethodA(int par1,int par2)
{
Console.WriteLine("Enter MethodA:{0},{1}",par1,par2);
MethodB(11, 18);
Console.WriteLine("Exit MethodA");
}
static void MethodB(int par1, int par2)
{
Console.WriteLine("Enter MethodB:{0},{1}", par1, par2);
Console.WriteLine("Exit MethodB");
}
static void Main(string[] args)
{
Console.WriteLine("Enter Main");
MethodA(15, 30);
Console.WriteLine("Exit Main");
Console.ReadKey();
}
}
输出结果为:
Enter Main
Enter MethodA:15,30
Enter MethodB:11,18
Exit MethodB
Exit MethodA
Exit Main
栈帧.png
方法重载
一个类中可以有一个以上的方法拥有相同的名称,这叫做方法重载,使用相同名称的每个方法必须有一个和其他方法不相同的签名
- 方法的签名有下列信息组成,它们在方法声明的方法头中:
- 方法的名称;
- 参数的数目;
- 参数的数据类型和顺序;
- 参数修饰符;
- 返回类型不是签名的一部分,而认为它是签名的一部分是一种常见的错误。
- 形参的名称也不是签名的一部分。
例:
class A
{
long Add(int a,int b) {return a+b;}
long Add(int a,int b,int c) {return a+b+c;}
long Add(float a,float b) {return a+b;}
long Add(long a,long b) {return a+b;}
}
类进阶
实例类成员
改变一个实例的字段拷贝的值不影响其他实例的拷贝的值
class D
{
public in num1;
}
class Program
{
static void Main()
{
D d1=new D();
D d2=new D();
d1.num1=10;d2.num1=25;
Console.WriteLine("d1={0},d2={1}",d1.num1,d2.num1)
}
}
输出结果
d1=10,d2=25
静态字段被类的所有实例共享,所有实例都访问同意内存位置,所以,如果该内存位置的值被一个实例改变了,这种改变对所有的实例都可见。
class D
{
int num1;
static int num2;
public void SetVars(int v1, int v2)//设置值
{ num1 = v1; num2 = v2; }
public void DisPlay(string str) //显示值
{ Console.WriteLine("{0}:num1={1},num2={2}",str,num1,num2); }
}
class Program
{
static void Main()
{
D d1 = new D();
D d2 = new D();//创建两个实例
d1.SetVars(2, 4);//设置d1的值
d1.DisPlay("d1");
d2.SetVars(22,14);//设置d2的值
d2.DisPlay("d2");
d1.DisPlay("d1");//再次显示d1
Console.ReadKey();
}
}
输出结果为:
d1:num1=2,num2=4
d2:num1=22,num2=14
d1:num1=2,num2=14
实例成员在实例被创建时开始存在,当实例被销毁时停止存在。然而,静态成员及时没有类的实例也存在并可以访问。
例:
class D
{
int num1;
static public int num2;
}
class Program
{
static void Main()
{
D.num2 = 4;//没有实例化
Console.WriteLine("num2={0}",D.num2);
Console.ReadKey();
}
}
输出结果
num2=4
静态函数成员
- 同静态字段一样,静态函数成员独立于任何类实例。即使没有类的实例,仍可以调用静态方法。
- 静态函数成员不能访问实例成员。然而,它们能访问其它静态成员。
成员常量
- 声明在类声明中;
- 不能再常量成员声明以后给他赋值;
- 成员常量对类的每个实例都是可见的,及时没有类的实例它们也可以使用;
- 常量在内存中没有自己的储存位置,而是在编译时被编译器替换。(ps:我一直以为是和值类型一样放在栈中呢( ̄▽ ̄)/);
属性
就像字段,属性有如下特征:
- 它是命名的类成员;
- 它有类型;
- 他可以被赋值和读取;
然而和字段不同的是,属性是一个函数成员。 - 他不为数据储存分配内存;
- 它执行代码;
属性是指定的一组两个匹配的,称为访问器的方法 - set访问器用于为属性赋值;
- get访问器用于从属性取值。
属性本身没有任何储存,访问器决定如何处理发进来的数据,以及什么数据应该被发送出去,属性使用一个字段做储存
set访问器接受他的输入参数value,并把他的值赋值给字段
get访问器只是返回字段的值
image.png
使用属性
写入和读取属性的方法与访问字段一样,访问器被隐式调用
int MyvAalue //属性声明
{
set{...}
get{...}
}
...
MyvAalue=5; //赋值隐式调用set方法
z=MyvAalue; //表达式:隐式调用get方法
属性和关联字段
只有get访问器的属性称为只读属性。
只有set访问器的属性称为只写属性。
两个访问器至少要有一个必须定义,否则编译器会产生一条错误信息。
静态属性和
属性也可以声明为static,静态属性的访问器和所有静态成员一样:
- 不能访问类的实例成员——虽然它们能被实例成员访问。
- 存在,不管类是否有实例;
-
当从类的外部访问时,必须使用类名引用,而不是实例名。
image.png
实例构造函数
实例构造函数是一个特殊的方法,他在类的每个新实例创建的时候执行。
- 构造函数用于初始化类实例的状态;
- 如果希望能从类的外部创建类的实例,需要声明构造函数为public;
- 构造函数的名称和类名相同。
-
构造函数不能有返回值。
image.png
静态构造函数
构造函数也可以声明为static,通常静态构造函数初始化类的静态字段
静态构造函数在下列方面和实例构造函数不同:
- 静态构造函数声明中使用static关键字。
- 类智能有一个静态构造函数,而且不能带有参数。
- 静态构造函数不能有访问修饰符。
class Class1()
{
static Class1()
{
... //静态构造函数的函数体
}
...
}
类既可以有静态构造函数也可以有实例构造函数。
静态构造函数示例:
class RandomNumber
{
private static Random RandomKey;//私有静态字段
static RandomNumber()//静态构造函数
{
RandomKey = new Random();//初始化
}
public int GetRandomNumber()
{
return RandomKey.Next();
}
}
class Program
{
static void Main()
{
RandomNumber a = new RandomNumber();
RandomNumber b = new RandomNumber();
Console.WriteLine(a.GetRandomNumber());
Console.WriteLine(b.GetRandomNumber());
Console.ReadKey();
}
}
输出结果为:
728118870
1501976979
析构函数
- 每个类只能有一个西沟函数;
- 析构函数不能带参数;
- 析构函数不能带访问修饰符;
- 析构函数和类有相同的名称,但以一个“~”字符做前缀;
- 析构函数只能对类的实例起作用,因此没有静态析构函数;
- 不能在代码中显示的调用析构函数,相反,他在垃圾收集过程中调用,当成垃圾收集器分型代码,并确定在代码中没有任何途径引用该对象。
例:
Class1
{
~Class1()
{
cleanuoCode;
}
...
}
- 如果不需要就不要执行析构函数,它们会带来性能上的开销。
- 西沟函数直营释放对象自己的外部资源,他不应该访问其它的对象,应为无法假定这些对象还有没有被收集。
析构函数本质上是Dispose代码的子集。
redonly修饰符
字段可以用redonly修饰符声明,其作用类似于声明一个字段为const,一但值被设定就不能改变
this关键字
this关键字在类中使用,是对当前实例的引用。它只能被用在下列类成员的代码块中:
- 实例构造函数;
- 实例方法;
- 属性和索引的实例访问器
this被用于下列目的: - 用于区分类的成员和本地变量,或参数。
- 作为调用方法的实参。
索引
索引是一组get和set访问器,类似于属性的访问器。
string this [int index]
{
set{SetAccessorCode}
get{GetAccessorCode}
}
网友评论