c#基础(一)
目录
typeof、default、checked、unchecked、sizeof
一.创建项目

图中解决方案可以包含多个项目,在文件夹中,项目放在解决方案文件夹下,并且会生成一个和项目同级的.sln文件,这个文件是解决方案的管理文件,可以点开后再创建项目
在vs中移除项目,文件夹中并不会删除,当在vs中不小心删除可以在文件夹中找到.sln文件找回
二.c#基础知识
控制台程序最基础结构:一个类型+一个静态main方法(静态不能去除)

2.1.变量
如输出函数就是写在system里面的方法,要使用这个命名空间下的内容需要在上方,using System,否则只会查找本文件中有无writeLine方法

为什么需要变量并且变量要规定不同变量类型?
- 变量:是为了分配一块空间给用户操作
- 要规定类型是因为,在内存中只有0和1,如果不规定类型例如:56对应asc码的A,而cpu无法知道你是需要A还是56,变量的类型是给了这块内存一个标签,存放的哪种类型,准确的拿到结果
- 在进行数据的运算时,赋值号左右的类型要相同
2.2数据类型、变量和对象
c#的数据类型分为2种五类,图中蓝色的为关键字同时也是数据类型,也就是说太常用c#把他作为关键字,当在vs中输入int 并且点击f12,可以看到int为值类型里面的结构体类型
值类型是没有实例的
引用类型的引用变量是存储实例内容地址的地址


2.2.1数据类型和变量类型的关系
int age = 25; // 变量 age 的类型是 int(变量类型),而 int 本身是数据类型
string name = "张三"; // 变量 name 的类型是 string(变量类型),string 是数据类型
- 说 “数据类型” 时,更侧重 “数据本身的特性”(如
int能存多大的数); - 说 “变量类型” 时,更侧重 “变量作为容器的特性”(如 “这个变量是
int类型的,所以只能存整数”)
2.2.2变量类型

小内存的数据放在大内存的数据类型里会浪费空间
大内存的数据放小内存的数据类型会丢失精度
当程序运行时,数据类型放在那里?
- 当程序运行时,程序从硬盘放到内存中,数据类型也在内存中
- 方法放在内存的栈区,方法太多会找出栈溢出,对象放在内存的堆区,堆的内存未回收会内存泄漏,但是c#会有垃圾回收机制,当没有调用这个对象时会自动回收。
- 局部变量也放在栈上
- 类中的成员变量放在堆上
2.2.3常量
- 使用const关键字
- 常量的值不能被修改
- 常量必须在定义时赋初值
- 常量作为成员常量时是类型的成员,即和静态变量相同,通过类型访问
const int a = 10;
2.3程序框的输入输出
console.readline(命令框输入)、console,writeline(打印)
其中输入/输出命令框的格式都是字符串类型,因此writeline可以使用+实现字符串的拼接;readline只能用字符串接受输入数据
输出一个拼接字符串
string name = "john";
string age = "20";
Console.WriteLine("{0}is{1}", name, age);
三、语句
语句只能出现在方法体中
语句以;结尾
3.1.声明语句
3.1.1.变量声明语句
int a=100;
//声明在赋值;
int b;
b=100;
//数值使用初始化器
int [] array={1,2}
图中:a后面的就是变量初始化器,而{1,2}是数值的初始化器
3.1.2常量声明语句
const int a=100
图中:常量必须要被初始化,且值不能再发生改变.
3.2标签语句
hell0: Console.WriteLine("hello");
goto hell0;
给语句添加标签,可以在goto中直接跳转到该语句,上面代码会一直执行.
3.3条件语句
3.3.1.三元运算符
Exp1 ? Exp2 : Exp3;
Exp1是否为真,若为真则执行条件Exp2,为假则执行Exp3
3.3.2.if/else语句
int a=10;
int b=5;
if(a>b){
Console.WriteLine("hello");
}
if(a>b)
Console.WriteLine("hello");
if语句的()只能放布尔表达式
if语句只能有一条嵌入式语句,因此有时只需要一条语句时可以不加{},而{}括起来的部分,称为块语句,编译器将块语句中内容视为一条语句;
在vs中输入if点击两下Tab键直接创建代码结构
3.3.3.switch语句
using System;
namespace MyApplication
{
class Program
{
static void Main(string[] args)
{
int day = 4;
switch (day) //这里的day就是expression
{
case 1:
Console.WriteLine("Monday");
break;
case 2:
Console.WriteLine("Tuesday");
break;
case 3:
Console.WriteLine("Wednesday");
break;
case 4:
Console.WriteLine("Thursday");
break;
case 5:
Console.WriteLine("Friday");
break;
case 6:
Console.WriteLine("Saturday");
break;
case 7:
Console.WriteLine("Sunday");
break;
}
}
}
}
- switch 语句中的 expression 必须是一个整型或枚举类型,或者是一个 class 类型,其中 class 有一个单一的转换函数将其转换为整型或枚举类型,没有浮点类型
- case后面的数必须和 switch 中的变量具有相同的数据类型,且必须是一个常量
- break必须每一个case都要有
- 可以选择在增加default 作为默认
- 并且如果两个case在逻辑上是相同范围,连起来写就行了,下面例子中如果case 8:后面写了表达式那么8必须加break,因为他变成了一个分支

3.3.4枚举类型和switch搭配
Level level = Level.Low;
switch (level)
{
case Level.Low:
break;
case Level.Mid:
break;
case Level.High:
break;
default:
break;
}
}
enum Level { Low, Mid, High }
枚举类型的定义要和类的定义写在一个层面
快速使用Switch来列举枚举中常量,输入switch,两下tab键,在条件中输入枚举变量level,回车自动生成.
编译时错误和运行时错误
编译时错误可以理解为代码的“语法考试”没通过。
运行时错误是程序“逻辑考试”不及格。
3.3.5try语句
- 使用try语句尝试执行,若有错误,就完全不执行try语句块中内容,执行catch中内容
- catch语句是分为有条件的和无条件的
- 下面代码若调用函数时输入不是数值形式的字符串try中内容执行时有错误,就不会改变a,b的值,并且执行catch语句,输出报错了
- finally语句无论是否有错误都会执行
try {
int a = 10;
double b = a / 0;
Console.WriteLine(b);
}
catch(Exception ex)//这个是所有错误的基类等效于直接写catch
{
Console.WriteLine("An error occurred during division.");
}
附加条件when
try
{
int divisor = 0;
int result = 10 / divisor; // 抛出 DivideByZeroException
}
catch (DivideByZeroException ex) when (ex.Message.Contains("zero")) // 仅当异常信息包含"zero"时才处理
{
Console.WriteLine("捕获到除零错误。");
}
3.4循环语句
3.4.1for循环/while循环/do-while语句
和c语言同
3.4.2foreach循环
1.使用迭代器来实现遍历集合
如下图:可以看到我们定义的数组输入Array类(所有定义的数组都属于这个类)
int[] array = new int[5] { 1, 2, 3,4,5 };
Console.WriteLine(array.GetType().FullName);//类型名称
Console.WriteLine(array is Array);//判断是否属于Array类
右键Array转到定义,所有的I开头的都是接口,所有实现了IEnumberable接口的类的实例都是可以被遍历的集合
public abstract class Array : ICloneable, IList, ICollection, IEnumerable, IStructuralComparable, IStructuralEquatable
右键IEnumberable接口,可以看到迭代器方法,所有可以被迭代的集合都能获得迭代器
IEnumerator GetEnumerator();
使用迭代器来实现迭代,定义了一个迭代器接口类型=定义的泛型返回的迭代器,调用迭代器的MoveNext方法判断是否能向后移动(读到6就不能移动了),Reset方法回到原点,.Current属性为当前值

2.使用for-each语句来实现变量集合
for-each语句就是迭代器的简便写法
int[] array = new int[5] { 1, 2, 3,4,5 };
foreach (var item in array)
{
Console.WriteLine(item);
}
3.5方法
c#中不能直接在命名空间下写函数,而必须作为某一个类的方法(如下图中的方法)
c#中方法的声明和定义是不分家的(如下图)
namespace ConsoleApp1
{
class Hero
{
public string Name;
}
class Circle
{//圆的面积
public double Math_Circle(double r){
double area=3.14*r*r;
return area;
}
//圆柱体体积
public static double Computer_cylinder(double area,double h)
{
double Volume = area * h;
return Volume;
}
}
class Program
{
static void Main(string[] args)
{
Circle C = new Circle();
//调用计算面积方法
double area = C.Math_Circle(2);
//调用计算圆柱体体积方法
double volume = Circle.Computer_cylinder(area, 3);
Console.WriteLine();
}
}
}
静态方法和实例方法
上图中的圆面积的计算方法就是实例方法,通过实例来调用,需要先把类示例化
上图中的圆柱体体积的计算方法就静态方法,通过类名来调用
构造器(构造函数)
每个声明的类都会有一个默认的构造器函数,代码如下
方法的重载
声明带有重载的方法,方法签名是由方法的名称,类型形参的个数、形参的个数、顺序、类型、种类来区分的,不包含返回类型
补充:(后面补充)
参数的种类:分为传值(普通形式),传输出(参数前加out),传引用(参数前加ref)
类型形参:泛型中内容,在函数名后<类型形参名>,可以实现在函数中定义类型形参名那种种类的参数
class Student
{
public Student(string name, int age)
{
this.name = name;
this.age = age;
}
//函数重载
public Student(string name, String age)
{
this.name = name;
this.age = int.Parse(age);
}
public string name;
public int age;
}
这个实例:是重载又是构造器
函数参数
值传递(常用)
效果等同浅拷贝
- 在传递时方法体里面会创建一个副本
- 实参和形式参数是值传递的,形式参数是实际参数的副本,两者地址不同但是存储相同数据
- 当然:引用类型作为参数传入函数,形参和实参地址不同但是都指向相同地址,下例
static void Main(string[] args)
{
Hero hero=new Hero() { Name="Superman"};
Somemethod(hero);引用类型作为值传递给形式参数
}
static void Somemethod(Hero hero) {
Console.WriteLine(hero.Name);
}
可为空参数
注意:不能不输入,只要有默认值的函数才能不输入
void Foo(string? s) | 必需参数,但允许为 null | Foo("x") ✅Foo(null) ✅Foo() ❌ |
ref关键字(常用)
特点:
值的地址传进函数。
注意:
在形参和实参位置都要加ref关键字
实际参数在传入前要先赋值
下例:值类型:y的数据也被改变,因为x和y实际是同一个地址
static void Main(string[] args)
{
int y = 100;
refdemo(ref y);
Console.WriteLine(y);//输出结果为110
}
static void refdemo(ref int x)
{
x += 10;
}
out关键字(常用)
特点:
- 值的地址传进函数。
- 可以在函数的参数列表做声明。
- 实际参数可以只声明不赋值,值传入时被丢弃掉了。
- 输出参数在方法体里面必须要赋值
使用out关键字,相当于多一个返回值,不创建副本,和ref的区别为实际参数可以不赋值,但是在函数内必须为形式参数赋值
private void Button_Click_1(object sender, RoutedEventArgs e)
{
execute(out int num);
Debug.WriteLine($"这是num:{num}");
}
private void execute(out int x) {
Debug.WriteLine($"{x}");//这里这样做直接报x未赋值
x = 100;
Debug.WriteLine($"{x}");
}
Params关键字
params参数必须是参数列表的最后一个参数
不使用数组参数,将数组引用传入函数
static void Main(string[] args)
{
int[] arr = new int[5] { 1, 2, 3, 4, 5 };
each(arr);
}
static void each( int[] arr)
{
foreach (int i in arr)
{
Console.WriteLine(i);
}
}
使用数组参数params,不需要定义一个数组,直接输入值
static void Main(string[] args)
{
each(1,2,3,4,5);
}
static void each(params int[] arr)
{
foreach (int i in arr)
{
Console.WriteLine(i);
}
键对值形式传参数
就是调用函数时,实际参数的写法可以使用键对值方式
特点:
不受参数输入顺序限制
输入类型是键对值形式,键名要和形参一致
static void Main(string[] args)
{
student(age: 20, name: "sun");
}
static void student(string name,int age){
Console.WriteLine("{0} is {1} years",name,age);
}
给形式参数一个默认值(常用)
在声明时给默认值,不输入对应参数就使用默认值
static void Main(string[] args)
{
student();
}
static void student(string name="sun",int age=18){
Console.WriteLine("{0} is {1} years",name,age);
}
扩展参数(暂时不用)
扩展方法必须写在静态类中,类名约定为SomeTypeExtension
必须是形参列表中第一个参数,需要对那个类型做扩展,用this修饰这个类型
方法必须是公有的,静态的
扩展方法是增加目标类型函数功能的,下面我们为double类型添加一个保留4位小数的功能
下面的代码为例使用扩展方法,Round这个方法第一个参数就是x本身:
internal class Program
{
static void Main(string[] args)
{
double x = 2.123456;
double y = x.Round(3);
Console.WriteLine(y);//输出结果位2.123
}
}
//扩展方法必须写在静态类中,类名约定为SomeTypeExtension
static class DoubleExtension
{//方法必须是公有的,静态的
public static double Round(this double num, int dec)//必须是形参列表中第一个参数,需要对那个类型做扩展,用this修饰这个类型
{
num = Math.Round(num, dec);
return num;
}
}
数组
数组的写法和c语言不一致【】放在名称前类型后,顺便在这里做声明和定义的区分
并且在定义时要给他分配空间
1.声明数组
datatype[] arrayName;//这里的datatype是数据类型
//声明一个int型数组
int[] arr1;
2.定义数组
(已声明的数组变量分配内存空间,并确定数组的长度(元素个数))
分配空间并且规定数组大小
arr1 = new int[3];
3.初始化数组
指定数字大小
// 定义长度为3的int数组,并初始化元素值(首次赋值)
int[] arr3 = new int[3] { 10, 20, 30 };
不指定数字大小但是给出初始值,给出初始值有简便写法,但是这个简便写法不能设置大小
// 省略长度,编译器自动根据初始值个数(4个)定义长度为4,并初始化
int[] arr4 = new int[] { 1, 2, 3, 4 };
// 更简化的写法(编译器会自动补全new int[])
int[] arr5 = { 5, 6, 7 };
操作符
基本概念
操作符不能脱离他关联的数据类型(整数除法和小数除法都是/,但是结果不同)
操作符优先级:
- 使用圆括号可以提示表达式优先级,圆括号可以嵌套
- 带有赋值操作符的运算顺序是先算运算符右边
- 除了带有赋值运算的操作符,其他都是从左到右

new操作符
new操作符:
1.创建实例,并且调用实例构造器,还能获得实例的地址,下例:new操作符创建了Hero实例,并且通过()调用了构造器,再将实例地址传给了引用变量hero;
Hero hero1 = new Hero();
2.调用初始化器(使用场景:只输入参数创建一个实例使用里面的一个函数,然后不使用引用参数获取他的地址,如下面例子,不使用引用参数获取他的值时,调用完成后内存被回收)
// 传统方式:先 new 再赋值
Person p1 = new Person();
p1.Name = "张三";
p1.Age = 25;
p1.Address = "北京市";
// new + 对象初始化器:一步完成创建和初始化
new Person
{
Name = "李四", // 初始化属性
Age = 30,
Gender = "男",
Address = "上海市" // 初始化公共字段
}.show();
3.给匿名类型创建对象(这个类是没有创建的,因此声明类型时间用var,参数就是输入的Id这些)
var student = new
{
Id = 1001,
Name = "赵六",
Score = 95.5
};
Console.WriteLine(student.Name)
typeof、default、checked、unchecked、sizeof
typeof:获取数据类型
default:获取默认值
checked():检查到溢出会报错,可以checked{},在{}内的代码都会被检查
unchecked():不检查是否会溢出,默认设置,使用方式和checked一样
sizeof:只能获取结构体类型所占字节的大小
->操作符:间接访问,只能放在不安全上下文使用(使用unsafe并且在上方项目->项目属性->生成->勾选允许不安全上下文),在c#中指针操作,取地址操作,使用指针访问成员只能只能操作结构体类型,不能访问引用类型
struct Sport
{
public String name;
public int score;
}
static void Main(string[] args)
{
unsafe {
Sport spo;
spo.score = 100;
spo.name = "football";
Sport *psport = &spo;
psport->score = 99;
Console.WriteLine(spo.score);
}
}
一元操作符
*和&:指针符号和取地址符号
+、-、~操作符:可以对数值取正和负,注意:计算机中同一类型的最大最小值是不对称的,负值绝对值通常大一,因此使用+,-操作可能会导致内存溢出。
~操作符:在二进制意义上每一位取反
!:取非
++、--:和c相同,在赋值时,在变量前后结果有区别
(T)x:类型转换,看2.10类型转换详解
乘法运算符(*)
这里要注意,乘法运算符的类型是由参与运算的变量类型来决定的,如果变量有高精度会按照高精度计算
int x=10;
int y=3;
int t=3;
int result=x/y; //结果是3
double result1=x/t //结果是3.33333
位移操作符(>>,<<)
数据的二进制数,左位移和右位移,由于二进制的特性,不产生溢出情况:左移就是×2,右移就是/2,
位移操作符补位规则:
左移:全部补0;右移:正数补0,负数补1;
关系操作符
知识引入:
- 编译时类型:变量声明时的类型(这里
a和a1的编译时类型都是Animal)在声明时已经确定。 - 运行时类型:变量实际指向的对象类型(
a指向Human对象,a1指向Animal对象)运行时类型在使用new创建时已经固定了
is:检验实例是否属于类,是返回true,失败返回false
as关键字既可以检验是否是属于该类,又可以实现转换引用变量类型;
转换规则是:只有当源对象的 “实际运行时类型” 是目标类型,或目标类型的派生类型时,转换才会成功,下文中a的实际运行类型就是human,而a1的实际运行类型是animal,在使用(引用变量 as human)时只有a可以成功,返回一个等于实际类型的编译时类型,下面代码只是返回了一个human类的引用变量,而a的编译类型并没有改变.
在继承关系中,子类实例可以安全转换为父类类型(向上转型:直接子类赋值给父类,如 Human → Animal),但父类实例不能直接转换为子类类型(向下转型,如 Animal → Human),除非父类变量实际指向的是子类实例(如 a 指向 Human 实例)
注意:父类变量可以指向子类实例,子类变量却无法指向父类实例
human h = new human();
if (h is human)
{
h.thike();//使用is运算符判断这个引用变量是否为这个类的实例
}
human h1 = new human();
if (h1 is human)
{
h1.Eat();//使用is运算符判断子类的引用变量也可以视为父类的实例
}
animal a = new human();//父类变量指向子类实例
animal a1 = new animal();//这种编译类型和实际类型都是animal的就不能父类转换为子类
human h2=a as human;//转换仅改变引用该对象的变量的编译时类型这里是h2—— 从基类(Animal)变为子类(Human),从而解锁对 “子类独有成员” 的直接访问
if (h2 != null)
{
h2.thike();
}
与或非逻辑操作符
&:按位与,是看二进制位,1和1才是真其他为假
|:按位或,有一个为真即为真
^:两位不一样才是真
条件与,条件或
||:或,操作对象和结果为布尔值
&&:与,操作对象和结果为布尔值
短路效应:当已经可以判断出结果时,后面的内容就会被跳过,如下:a>b为假,c++不会运行,c的值不变,最外层结构为A||B时,A为真则跳过判断B,最外层结构为A&&B时,A为假就会跳过A
int a = 10;
int b = 20;
int c = 10;
if (a > b && c++ > 10)
{
}
Console.WriteLine(c);
?操作符
举例说明:
int? a = null;
var x = a?? 10;
上面代码
int ?a=null含义为定义一个可空的int类型元素a=null(int?是语法糖,全写为 Nullable<int>)
??是空合并运算符,若a为空返回10,若不为空返回a的值
2.10类型转换
2.10.1.隐式类型转换(直接赋值)
不丢失精度的类型转换:低精度向高精度转换,要是高向低不显式转换会报错。
子类向父类的类型转换:直接把子类引用类型赋值给父类的引用类型,注意:转换后的父类引用类型只能访问他自己有的成员方法和成员变量。
static void Main(string[] args)
{
human h = new human();
animal a = h;
//直接将h赋值给父类的引用类型,注意,父类只能调用他自己有的方法和属性
a.Eat();
}
}
class animal
{
public void Eat()
{
Console.WriteLine("animal is eating");
}
}
class human : animal {
public void thike() {
Console.WriteLine("human is thiking");
}
}
2.10.2显式类型转换
数字变量间高精度转低精度
使用类型转换关键字,一般是同类型数据高精度向低精度转换
double x = 3.14159;
int y = (int)x; // 显式转换:double → int(小数部分被截断,y=3)
char和整数之间的转换
char c = 'A';
int code = (int)c; // 结果:65('A'的Unicode编码)
convert类
集成了Tostring和Parse方法,直接在编译器.来查看有那些方法
Tostring方法(object类实例方法)
这个方法是写在object类中的在上文数据类型中可以看到,所有的数据类型都是他的派生,因此都有Tostring方法,Tostring是实例方法,通过实例调用
int x = 20;
x.ToString();
Parse方法(object类静态方法)
将字符串类型转换为目标类型,Parse是静态方法,通过类名称调用,注意:只能转换格式正确的,如:123转换为int类型
// int.Parse:字符串转整数
string intStr = "123";
int num = int.Parse(intStr); // 转换成功,num = 123
// double.Parse:字符串转双精度浮点数
string doubleStr = "3.1415";
double pi = double.Parse(doubleStr); // 转换成功,pi = 3.1415
// decimal.Parse:字符串转高精度小数(适合金额)
string decimalStr = "199.99";
decimal price = decimal.Parse(decimalStr); // 转换成功,price = 199.99
类和对象
类怎么定义和实例化
不调用实例构造器方式
class Hero
{
public string Name;
}
class Program
{
static void Main(string[] args)
{
//这里的hero1叫引用变量
Hero hero1 = new Hero();
hero1.Name = "Superman";
Console.WriteLine("Hello, " + hero1.Name + "!");
}
}
调用实例构造器方式
static void Main(string[] args)
{
Hero hero=new Hero() { Name="Superman"};
}
class Hero
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
}
修饰限定符及其默认值
C# 封装根据具体的需要,设置使用者的访问权限,并通过 访问修饰符 来实现。
一个 访问修饰符 定义了一个类成员的范围和可见性。C# 支持的访问修饰符如下所示:
- public:所有对象都可以访问;
- private:只在类的内部可以访问,即使是类的实例也不能访问它的私有成员;
- protected:只有该类对象及其子类对象可以访问
- internal:同一个程序集的对象可以访问;
- protected internal:访问限于当前程序集或派生自包含类的类型。

- 顶层类型指直接定义在
namespace中的类、结构体、接口、枚举、委托等,其默认访问修饰符为internal。 - 嵌套类型指定义在类、结构体内部的类型(如类中的嵌套类),其默认访问修饰符为
private - 类(
class)和结构体(struct)的成员(字段、方法、属性、事件、构造函数等),默认访问修饰符为private
类的成员
在visual studio中使用对象.的方式可以看到有那些类的成员,扳手图标表示属性,盒子表示方法,闪电表示事件

属性
作用:是为了避免数据污染,属性是字段的包装器。
类属性是有默认值的,而写在progress类中的mian方法里面的变量称为本地变量,本地变量是没有默认值的
属性的完整写法
快捷键写法:输入propfull(prop是property的缩写),按两下tab键,使用tab键选择给我们修改的地方,快速生成
注意:
属性的set和get是没有();
set方法的value是不需要自己定义的,等于给他赋的值
private int age;
public int Age
{
get
{
return this.age;
}
set
{
if (value >= 0 && value <= 120)
{
this.age = value;
}
else
{
throw new Exception("age value is error");
}
}
}
字段(成员变量)
静态字段和实例字段
静态字段用来描述一个类型共有的属性,实例字段描述一个实例单独的属性
静态字段和类同级,无论多少个实例,一个类中同名静态字段只有一个
静态字段通过类名调用,实例字段通过实例调用
static void Main(string[] args)
{
Student stu=new Student();
stu.name = "John";
stu.age = 20;
Student stu2 = new Student();
stu2.name = "Mary";
stu2.age = 22;
double average_age = Student.average_age=(stu.age+stu2.age)/ (double)2;
Console.WriteLine(average_age);
}
class Student
{
public static double average_age;
public string name;
public int age;
}
字段和属性的关系?
首先辨析属性的两种类型的写法
全写方式的属性,给属性的值的实际存储地址是字段,这个很直观
隐式写法的属性,存储的地址也是字段,这个字段是不能直接访问的,但是机理是这样
public int MyProperty { get; set; }
全写属性set方法的value值
我们注意set和get方法是没有参数列表的,但是实际上当我们给属性赋值的时候调用的是set方法,并且传入了要赋的值,value就是传入参数的值
属性和字段的使用方法是一致
this关键字
this只能在实例成员(实例方法、实例属性、实例构造函数)中使用,静态成员(静态方法、静态构造函数)中不能使用this(因为静态成员属于类,不属于某个实例)this的核心是 “当前对象的引用”,就是指向当前对象的指针,它的主要作用是:区分同名成员与局部变量(加this的是成员)、传递当前对象、调用其他构造函数、定义扩展方法。this写在类的定义中,是 **“提前声明” 了一种 “对当前实例的引用方式”**,但此时还没有具体的实例,所以它是 “抽象的占位符”。- 当通过
new创建实例后(比如zhangsan = new Person()),这个实例在内存中存在了。此时调用实例的方法(zhangsan.SayHello()),this就会动态绑定到这个具体实例(zhangsan),所以this.Name才能正确拿到 “张三
更多推荐

所有评论(0)