c#图解教程第5版 -- 读书笔记(4)
3 格式化字符串
大括号包裹一个索引
Console.WriteLine("Hello, {0}!", "huang");
大括号包裹一个变量
string name = "huang";
Console.WriteLine($"Hello, {name}!");
3 格式化数字字符串
格式化的字符串必须指明对象是谁,用索引或者变量名,然后,
分割对齐,:
分割格式化
{index,alignment:format}
-
对齐说明符表示字段中字符的最小宽度(可选)
- 由一个整数组成
- 符号表示对齐格式,正数是右对齐,负数是左对齐
- 表示的字符比对齐说明小,其余用空格填充
- 表示的字符比对齐说明大,对齐说明被忽略
-
格式字段指定数字应该以哪种形式表示,
- 格式说明符是单个字母字符
- 精度说明符是可选的,由1-2个数字组成,实际意义由格式说明符决定
3 注释
多行注释
/*
开始,*/
结束
单行注释
//
开始,直到行结束
文档注释
///
每行都需要这个开始
可以写XML,用于产生程序文档
4.5 预定义类型
预定义类型会直接映射到.NET类型
4.6 用户定义类型
- class
- struct
- array
- enum
- delegate
- interface
4.8 值类型和引用类型
数据项的类型定义了存储数据需要的内存大小及组成该类型的数据成员,还决定了对象在内存中的存储位置
- 值类型:只需要一段单独的内存,用于存储实际数据
- 引用类型:一段存储实际数据,一段存储指向实际数据的位置(指针)
5.8 访问修饰符
- private(默认级别):只能从声明它的类内部访问
- public:可以被系统中其他对象访问
- protected:受保护的,只能由该类的派生类访问
- internal:内部,对同一个程序集的类都可见
- protected internal:受保护的内部,对派生类或者同一个程序集的类可见
6.3.1 类型推断
var i = 32;
var s = new SomeClass();
6.11 值参数
- 在栈中为形参分配空间
- 将实参的值复制给形参
6.12 引用参数
必须在形参和调用的位置加上ref
关键字
- 不会在栈中为形参分配内存
- 形参的参数名将作为实参变量的别名,指向相同的内存位置
6.13 引用类型作为值参数和引用参数
- 作为值参数:如果在方法内部创建一个新的对象并赋值给形参,那么形参和实参之间的关联就会断开,方法返回之后,新对象将销毁
- 作为引用参数:如果在方法内部创建一个新的对象并赋值给形参,方法返回之后,实参将指向新对象
6.14输出参数
用于从方法内部把数据传出到调用代码
- 必须在形参和调用位置使用
out
关键字 - 实参必须是变量
与引用参数类似,输出参数是实参的别名,与引用参数不同的是:
- 在方法内部,输出参数必须赋值之后才能读取它
- 在方法内部,在方法返回之前,所有可能的路径都必须为所有的输出参数赋值
// 可以不预先声明输出参数,直接在调用时声明
func(out MyClass a1, out int a2);
Console.WriteLine($"{a1}, {a2}");
6.15 参数数组
// 可变长参数
void func(params int[] intVals)
{
...
}
6.17 ref局部变量和ref返回
ref局部变量
允许一个变量是另一个变量的别名
- 即使引用的是值类型,也可以创建
- 对任意一个变量的赋值都会反映到另一个变量上,即使是值类型
int x = 1;
ref int y = ref x;
ref返回
一种使方法返回变量引用而不是变量值的方法
ref int func()
{
int x = 1;
return ref x;
}
一般使用:
class Simple
{
private int Score = 5;
public ref int RefToValue()
{
return ref Score;
}
public void Display()
{
Console.WriteLine($"Value inside class object: {Score}");
}
}
class Program
{
static void Main()
{
var simple = new Simple();
simple.Display();
ref var v1Outside = ref simple.RefToValue();
v1Outside = 10;
simple.Display();
}
}
注意:
- 只能返回原先就在调用域内的位置,或者字段,所以它不能指向方法的局部变量
- ref局部变量只能被赋值一次,也就是一旦初始化就不能指向不同的位置了
- 即使将方法声明是ref,如果在调用时省略了ref,则返回的是值
- 如果将ref局部变量作为常规的实参传递,方法只能获得副本,尽管ref局部变量包含指向存储位置的指针
6.18 方法重载
相同的名称,不同的签名
- 方法的名称
- 参数的数目
- 参数的数据类型和顺序
- 参数修饰符
7.1 类成员
7.2 成员修饰符顺序
[特性] [修饰符] 核心声明
可选:
- 修饰符
- 有,就必须放在核心声明前
- 多个修饰符,可以任意顺序
- 特性
- 有,必须放在修饰符和核心声明前
- 多个特性,可以任意顺序
修饰符:public
,private
,static
,const
7.9 成员常量
class MyClass
{
const int PI = 3.14;
}
表现得像一个静态成员,对类的每个实例可见,即使没有类的实例也可以使用,唯一不同时,在编译时会被替换,类似C++中的#define
7.10 属性
与字段相似:
- 命名的成员
- 有类型
- 可以赋值和读取
不同的:
- 不一定为数据存储分配内存
- 它执行代码
属性时一组匹配的、命名的、称为访问器的方法
int MyProperty
{
get { ... }
set { ... }
}
// 或者使用lambda表达式
int MyProperty
{
get => ...;
set => ...;
}
MyProperty = 5; // 隐式调用set
z = MyProperty; // 隐式调用get
注意:
- 属性本身没有任何存储
- 访问器决定如何处理发送进来的数据,以及将什么数据发送出去
- 可以只定义其中一种访问器,完成只读/只写功能
- 类中没有关联的字段时,可以
自动实现属性
,只需要不声明字段,get/set都没有方法体,只有;
结尾
7.15 readonly修饰符
作用类似const
,用于修饰字段,一旦值被设定就不能改变
- 可以在字段声明中初始化,或者在类的构造函数中初始化
const
字段必须编译时就决定,readonly
可以在运行时决定readonly
可以是实例字段或者静态字段,const
是静态的
7.17 索引器
索引器是一组get/set访问器,与属性类似
int this [int index]
{
get { ... }
set { ... }
}
索引器可以重载
7.18 访问器访问修饰符
一般情况下,访问器的访问级别和属性或者索引器一致,不过也可以不同
- 仅当有两个访问器时,才能设置访问修饰符
- 只有有一个访问器有访问修饰符
7.19 分部类和分部类型
类的声明可以分割成几个分部类的声明
- 每个分部类的声明都含有一些类成员的声明
- 类的分部类可以在一个文件也可以在不同文件
- 类必须标注为
partial class
7.20 分部方法
- 定义分部方法
- 给出签名和返回值
- 实现部分是一个
;
- 实现分部方法
- 给出签名和返回值
- 以普通语句块形式实现
注意:
- 返回类型必须是
void
- 签名不能有访问修饰符,所以分部方法是隐式私有的
- 参数列表不能有
out
- 声明必须包含
partial
8.4 屏蔽基类的成员
要让编译器知道你在故意屏蔽成员,在前面加上new
关键字
基类访问:base
8.7 基类构造函数
public DerivedClass(int x, int y) : base(y, x)
{
...
}
8.7 类访问修饰符
- pblic:系统内任何程序访问
- internal:类自己所在程序集内的类看到,默认级别
8.10 抽象成员
8.12 密封类
- 不能当作基类
sealed
标注
8.13 静态类
- 成员全是静态的
- 类本身
static
标注 - 没有实例构造函数,可以有静态构造函数
- 隐式密封
8.14 扩展方法
编写的方法与声明它的类之外的类关联
static class ExMyData
{
public static int AA(this MyData md)
{
...
}
}
注意:
- 必须是静态类
- 方法参数第一个必须是包含
this
的被扩展类实例
8.15 命名约定
9.2.4 字符串字面量
不转义
string s1 = @"hello \t world";
11.1 什么是结构
- 类是引用类型,结构是值类型
- 结构是隐式密封,不能作为基类
12.1.1 设置枚举底层类型和显式值
enum App : ulong
{
A = 10,
B = 11;
C = 11;
}
12.2 位标记
[Flags]
enum S : uint
{
S1 = 0x01,
S2 = 0x02,
S3 = 0x04,
S4 = 0x08
}
按位表示
[Flags]
提供一些便利功能
13.5 实例化数组
int[]
,一维数组int[,]
,二维数组int[,,]
,三维数组
实例化
int[] arr1 = new int[4];
int[,] arr2 = new int[3,3];
int[,,] arr2 = new int[3,3,3];
数组访问
int[] arr1 = new int[4];
arr1[2] = 1;
int[,] arr2 = new int[3,3];
arr2[1,1] = 2;
int[,,] arr2 = new int[3,3,3];
arr3[1,1,1] = 3;
初始化数组
int[] arr1 = new int[] {1, 2, 3};
int[,] arr2 = new int[,] {{1, 2}, {3, 4}};
快捷初始化
int[] arr1 = {1, 2, 3};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?