# 基础一
- .NET 程序编译过程:
写的是源代码(.cs 等)——CLS 编译 —>CIL (通用中间语言)(.exe .dll)——CLR 编译 ——> 0 1
计算机不认识源代码
CLS (公共语言规范) 编译的目的在于 跨语言
CLR (公共语言运行库) 编译优点在于 运行优化 和 跨平台 - 1 字节:有符号:sbyte 无符号:byte
1 字节有八位,所以长度为 2 的 8 次方 - 浮点数:
float:1.2f (7 位精度)
double:1.2d (15-16 位精度)
decimal:1.2m (28-29 位精度) static void Main(string[] args){ }
static: 是将 Main 方法声明为静态, 是应用程序的入口。
void: 说明 main 方法不会返回任何内容。
string [] args:这是用来接收命令行传入的参数。string [] 是声明 args 的数据类型,可以存储字符串数组。- 标准数字字符串格式化:https://blog.csdn.net/lfq761204/article/details/116458224
WriteLine(): https://blog.csdn.net/yousss/article/details/84790040 #region 方案名
可以将每个方案分隔开- WriteLine:输出并换行
Write:输出 - 当多种变量参与运算,结果的类型自动向大的类型提升
- 创建一个随机数工具
Random Random_1 = new Random();
产生一个随机数,括号内表示范围(包括最小值,不包括最大值)int TrueNumber = Random_1.Next(1, 101);
Console.WriteLine("Hello, World!");
Console 译为:控制台
Console 是:类【工具】
WriteLine 是:方法【相当于动词的功能】
Title 是:属性【相当于名词的修饰】
类。方法 (); 调用语句【使用某个类中的功能】- 类中定义的函数被称为方法
一个方法处理一个功能 - 对于一个项目 吧要实现的功能和细节都实现,然后拆分成多个方法(这是必要的)
- 不同条件下解决同一类问题,使方法的命名相同参数不同 (方法重载),调用者只记住一个方法即可。例如:
void Test();
void Test(int a);
- bool 类型在 Java 中写为 Boolean(这个才是完整的写法)
# 基础二
- params
params type[] name 参数数组
对于方法而言 参数数组 就是数组
对于调用者而言 参数数组 可以传递 同类型数组 或 同类型数据 或 不传递数据
可以简化调用者的代码
例:Console.WriteLine("{0}{1}", 1, 2);
而不是Console.WriteLine("{0}{1}",new object[]{ 1, 2 });
# 数据类型(局部变量)
C# 中有两种数据类型:值类型、引用类型
方法在栈中执行,所以声明的变量也在栈中
值类型直接存储数据,所以数据存储在栈中
引用类型在栈中存储数据对象的引用(也就是地址)这个地址是对应的数据在堆中的地址 数据本身存储在堆中实例 1:
int[] Arr_1 = { 1 };
int[] Arr_2 = Arr_1;
// 在 C/C++ 中是不允许这样赋值的
// 这一步直接让 Arr_2 的地址变成 Arr_1 的地址了
// 把 Arr_1 在栈中储存的地址赋值给了 Arr_2 在栈中储存的地址
// 所以在堆中改一个数据,两个数组中的元素一起变
Arr_1[0] = 2; // 对堆中储存的数据进行改变
Console.WriteLine(Arr_2[0]);
// 对数组 new type [] 会在堆中新建一个数据 数组的引用也会指向这个新的数据的地址
Arr_1 = new int[] { 2 }; // 对栈中储存的引用进行改变
Console.WriteLine(Arr_1[0]);
实例 2:
string[] str1 = new string[1] { "18+" };
// 栈中 string [] 储存 new string [] 的引用
// 堆中 new string 储存 "18+" 的引用
// "18+" 储存在堆中
实例 3:
array = new Array
替换引用,方法外(实参)不受影响string 和 object 类型在第一次赋值以后,在堆中的数据是只读,无法修改堆中的数据
# 三种参数
值参数 和 引用参数
值参数 按值传递,传递实参变量储存的内容
引用参数 按引用传递,传递实参变量自身的地址 与 C++ 中的引用相同例 1:参数声明
值参数int a
引用参数ref int a
例 2:引用类型的使用
int A = 1;
ref int RefA = ref A;
// 相当于 C/C++ 中的
int A = 1;
int* RefA = &A;
例 3:unsafe
C# 中的指针与 C/C++ 的指针相同,但 C# 将指针的使用定义为 不安全(不确定是否安全)的代码unsafe
{
int B = 1;
int* RefB = &B;
}
输出参数 与 引用参数
二者原理相同,作用不同输出参数 的作用:返回结果 方法只有一个返回值,而输出参数可以让方法返回多个值
引用参数 的作用:改变数据区别 1:方法内必须为输出参数赋值,输出参数必须返回结果
区别 2:输出参数传递之前可以不赋值例 1:
输出参数out int a
引用参数ref int a
例 2:
输出参数string A = Console.ReadLine();
# 基础四
type.Parse();
如果输入的字符的类型与这个 type 不同,就会无法转换,并报错type.TryParse();
如果输入的字符的类型与这个 type 不同,就会取消转换,并给输出参数返回 0
它有两个返回值 方法整体返回 Bool 类型(转换成功与否),方法内部返回转换结果给输出参数
例:int.TryParse(Console.ReadLine(), out int A);
装箱:将值类型传递给 object 类型
拆箱:将 object 类型传递给值类型
拆箱 和 装箱 比较消耗性能,所以尽量避免使用(例:方法重载,泛型)- 因为目前电脑性能都很好,所以编程的时候多次使用 拆装箱 也不会怎么样
但是,面试的时候不能这么说 - 例:
int num = 100;
// 这个过程没有发生 拆装箱
string str_1 = num.ToString();
string str_2 = "12" + num;
// VS2020 的反编译
// 内部代码:string str_2 = string.Concat ("12", num.ToString ());
// "12" 是 string 类型
// 执行重载 string Concat (string? str0, string? str1)
// 所以这个过程没有拆装箱
// VS2017 的反编译
// 内部代码:string str_2 = string.Concat ("12", num);
// 方法 string.Concat () 的形参是 object
//num 是 int 类型,需要 int --> object --> string 的转换
// 所以这个过程发生了拆装箱
- 因为目前电脑性能都很好,所以编程的时候多次使用 拆装箱 也不会怎么样
字符串池 和 字符串的不可变性
给字符串赋值时,会在字符串池中检索有没有相同的字符串
如果有,将引用传给它
如果没有,开辟一块空间,将 " " 中的字符串放进去
在程序执行的过程中,字符串池中的字符串不会改变程序运行时字符串在堆中储存的数据都是不可变的
每一次赋值都会开辟一个新空间,替换引用
频繁地给字符串类型赋值,会在堆中堆积很多垃圾数据,必要时会启用 GC ,但 GC 很消耗性能
应尽量避免频繁地给字符串等类型赋值
少用string_1 + string_2
的方式做字符串拼接StringBuilder 可以完美的应对 字符串频繁的增删改查 的情况
//10 是开辟空间的容量 (单位:字节) 可以为空 (默认大小)
StringBuilder Str_1 = new StringBuilder(10);
for (int i = 0; i < 10; i++)
{
Str_1.Append(i);
}
Str_1.Append("1212121");
// 这时 10 字节不够用了
// 它会自动再开辟一个更大的空间,将所有数据拷贝进去,并替换引用 (这样就产生垃圾数据了)
string result = Str_1.ToString();
# 基础五
- 枚举 enum
枚举属于数据类型中的值类型
使用枚举可以限定形参 (或实参) 输入的数据
与 C/C++ 中的枚举类似 - 在类型后加?表示字段可以为空
- 例:
int? a = null;
此时 a 不再是值类型了,而是可空值类型 - 例 2: 有一个变量 b, 它的类型是自定义的某个 类 的类型
将 b 变为 可空 后,如果要赋值,应该改使用 b.Value
- 例:
- 继承
多个类有相同的代码,且属于同一概念(比如:学生和老师都有名字,都属于人类)
子类可以使用父类,父类只能用自己的 - 类中有 实例成员 和 静态成员
静态成员 属于类,类被加载时初始化,并且只会初始化一次
实例成员 属于对象,对象被创建时初始化,每个对象只初始化一次
静态成员 的优先级高于对象,被所有对象共享,常驻内存
静态代码块只能访问静态成员 - 静态构造函数:初始化类的静态成员
没有访问级别
类加载时调用一次 - 实例构造函数:初始化类的实例成员
- 成员的初始化在构造函数中完成,不要定义成员直接 new
- 加载一个类,类中的实例化数据成员储存在堆中,静态数据成员储存在静态区
类被加载后,所有的代码都会放在静态区 - 实例方法能访问所有成员
静态方法只能访问静态成员
实例方法在栈中执行时会在栈中开辟一块空间(叫:栈帧),栈帧里有必定一个参数(对象的引用)
通过引用调用实例方法,都会自动的传递引用
因为一个对象对应一组实例成员,所以传递引用的作用在于:要知道是谁调调用了实例方法
调用静态方法,由类直接调用,没有对象(也就没有这个参数),所以静态方法只能访问静态成员
换一个理解方式:静态方法的优先级更高,执行这段代码的时候根本不存在对象 - 结构体
结构体与类最大的区别是:结构体是值类型,类是引用类型- 与类的其他区别:
- 结构体不能包含无参数的构造函数(因为它自带无参数的构造函数)
- 结构体的构造函数使用字段,必须先给字段赋值(初始化)
也就是说:使用自动属性的字段(例:public int Score { get; set; }
)时,必须调用无参数的构造函数
调用无参数的构造函数时会为自动属性的字段赋值 - 结构体的实例字段声明时不可赋值
- 与类的其他区别:
- internal 限制
只能在同一个命名空间中可以调用,出来这个命名空间就必须引用这个程序集和该命名空间才能调用里面的类和方法