C# 学习笔记(一)
最近开始学 C#,计划五一五天速成一下,记录一下学习笔记,主要记录和 C++ 的区别,其他和 C++ 相同的就简单过去了,目前主要看的是《C#图解教程》
C# 编程概述
一个简单的 C# 程序
using System; // 类似 using namespace std, Console 类在 System 命名空间下
namespace Simple
{
class Program
{
static void Main() // 类型 int main(), 是程序的起点
{
Console.WriteLine("Hello, World!");
}
}
}
注:
-
C# 9.0 的顶级语句(Top-level statements):
从 C# 9.0 开始,支持省略传统的
class Program
和static void Main()
结构,允许直接在文件顶层编写执行代码。编译器会自动为你生成这些模板代码。 -
隐式全局 using 指令:
.NET 6+ 的项目模板默认启用了"隐式全局 using"功能,常见的命名空间(如System
)会被自动导入。
标识符
基本和 C++ 差不多,只不过多一个@字符,且只能放在标识符首位,但不推荐作为常用字符。
关键字
C++ 独有关键字(无 C# 对应)
关键字 | 用途 |
---|---|
template |
泛型编程(编译时展开) |
typename |
声明模板类型参数 |
mutable |
允许修改被const 修饰的成员变量 |
friend |
友元类/函数(突破封装) |
volatile |
禁止编译器优化(多线程场景) |
asm |
内联汇编代码 |
delete |
释放堆内存 |
explicit |
禁止隐式构造函数转换 |
C# 独有关键字(无 C++ 对应)
关键字 | 用途 |
---|---|
var |
类型推断(编译时确定类型) |
async /await |
异步编程模型(简化异步操作) |
delegate |
定义委托(类型安全的函数指针) |
event |
声明事件(基于委托) |
lock |
线程同步锁 |
checked |
启用算术溢出检查 |
unchecked |
禁用算术溢出检查 |
sealed |
禁止类被继承或方法被重写 |
params |
可变参数列表(如params int[] ) |
程序中输出文本
Console
类中有 Write
和 WriteLine
,前者不会自动加换行,后者会自动加换行
格式化字符串
类似 Python 或者 C++ 的 std::format
Console.WriteLine("Two sample integers: are {1} and {0}", 5, 10);
// Two sample integers: are 10 and 5
C# 6.0 引入了一种更简单的方式称为字符串插值,类似 f-string,必须在字符串前面加 $ 符号
int var1 = 5;
int var2 = 10;
Console.WriteLine($"Two sample variables: {var1} and {var2}");
// Two sample variables: 5 and 10
格式化数字字符串
这里和 Python 或者 C++ 的 std::format
有些区别
Python 和 C++ 是在冒号后面用 <
、>
和 ^
表示左右和居中对齐,而 C# 的对齐是在 ,
后面用宽度的正负来表示左右对齐,类似 C++ printf
的格式化字符串,但不能自定义填充字符
Console.WriteLine($"|{5,10}|");
Console.WriteLine($"|{5,-10}|");
// | 5|
// |5 |
其他格式化大同小异
double PI = 3.1415926;
Console.WriteLine($"{PI,-10:G}--General");
Console.WriteLine($"{PI,-10}--Default,Same as General");
Console.WriteLine($"{PI,-10:F4}--Fixed-point, 4 decimal places");
Console.WriteLine($"{PI,-10:C}--Currency");
Console.WriteLine($"{PI,-10:E3}--Exponential, 3 decimal places");
Console.WriteLine($"{12345,-10:x}--Hexadecimal Integer");
// 3.1415926 --General
// 3.1415926 --Default,Same as General
// 3.1416 --Fixed-point, 4 decimal places
// ¥3.14 --Currency
// 3.142E+000--Exponential, 3 decimal places
// 3039 --Hexadecimal Integer
注释
单行和多行注释和 C++ 相同,但多一个文档注释,包含 XML 文本,用于产生程序文档
/// <summary>
/// This class does...
/// </summary>
class Program
{
...
类型、存储和变量
C# 程序是一组类型声明
- 与 C/C++ 的对比:
- C 程序:以函数为中心,全局函数和基本数据类型构成程序。
- C++ 程序:以类和函数混合为核心,支持面向对象。
- C# 程序:完全面向类型,所有功能必须封装在类型内
- C# 程序或 DLL 的源代码是一组类型声明
- 对于可执行程序,类型声明中必须有一个包含
Main
方法的类 - 命名空间是一种将相关的类型声明分组并命名的方法
预定义类型
C# 提供了 16 种预定义类型,其中包括 13 种简单类型和 3 种非简单类型。所有预定义类型都直接映射到底层的 .Net 类型,使用 .Net 的类型名称也合法但不鼓励。
- 简单类型
- 非数值类型
- bool
- char
- 数值类型
- 整数类型
- 8 位
- sbyte 和 byte
- 16位
- short 和 ushort
- 32 位
- int 和 uint
- 64 位
- long 和 ulong
- 8 位
- 浮点类型
- decimal (高精度浮点数,占 16 个字节)
- float
- double
- 整数类型
- 非数值类型
- object(所有其他类型的基类)
- string
- dynamic(使用动态语言编写的程序集时使用)
用户定义类型
除此之外有 6 种类型可以由用户创建
- 类类型 (class)
- 结构类型 (struct)
- 数组类型 (array)
- 枚举类型 (enum)
- 委托类型 (delegate)
- 接口类型 (interface)
栈和堆
栈和 C++ 的区别不大,堆的话因为 C# 有垃圾回收,所以不需要显式也不能显式的删除堆里的数据,因此也没有 delete 关键字
值类型和引用类型
C# 的引用类型更接近于 C++ 的指针而不是引用,只不过没有 *
号,引用类型存储的实际数据总是位于堆上,而引用也就是地址部分可在堆上也可在栈上,且引用类型可以为 null
预定义类型中,三个非简单类型都是引用类型,简单类型里只有三个浮点类型和 char
是引用类型
用户定义类型中只有 struct
和 enum
是值类型,其他都是引用类型
变量
C# 提供了 4 种变量
- 局部变量:该方法的作用域保存临时数据,不是类型的成员
- 字段:保存和类型或类型实例相关的数据,是类型的成员
- 参数:用于从一个方法到另一个方法传递数据的临时变量,不是类型的成员
- 数组元素:(通常是)同类数据项构成的有序集合的一个成员,可以为局部变量,也可以为类型的成员
变量声明
声明和初始化基本类似 C++,不过 C# 更严格,对于未初始化的局部变量,在赋值前不能使用,编译器会报错。
变量类型 | 存储位置 | 自动初始化 | 用途 |
---|---|---|---|
局部变量 | 栈或者栈和堆 | 否 | 用于函数成员内部的局部计算 |
类字段 | 堆 | 是 | 类的成员 |
结构字段 | 栈或堆 | 是 | 结构的成员 |
参数 | 栈 | 否 | 用于把值传入或传出方法 |
数组元素 | 堆 | 是 | 数组的成员 |
静态类型和 dynamic 关键字
C# 的类型基本都是静态类型,但是因为 IronPython 和 IronRuby 之类的脚本语言是动态类型的,而它们是 .Net 语言,所以 C# 需要能够使用这些语言编写的程序集,因此增加了 dynamic
关键字,代表一个特定的 C# 类型,它知道如何在运行时解析自身。
编译器不会对包含类型 dynamic
的表达式的操作进行解析或类型检查。编译器将有关该操作信息打包在一起,之后这些信息会用于在运行时评估操作。 在此过程中,dynamic
类型的变量会编译为 object
类型的变量。 因此,dynamic
类型只在编译时存在,在运行时则不存在。
可空类型
引用类型可以设置为 null
,而值类型为不可空类型。
类的基本概念
类的概念和声明基本类似于 C++,不过有一个小细节就是 C# 的类的定义后面是不需要分号的,而 C++ 需要,我想主要是因为 C# 不存在全局变量,在类外是没法声明变量的。
类成员
字段
等同于 C++ 的成员变量,前面提到过 C# 的局部变量不会自动初始化,未初始化或赋值直接使用会报错,但字段是会自动初始化的,默认值类似 C++,值类型基本都是 0,引用类型则是 null
方法
等同于 C++ 的成员函数,同样的,C# 也没有全局函数的概念,所有的函数或者说方法都只能在类中声明,值得一提的是,书中提到:和 C/C++ 不同,C# 方法没有默认的返回值类型。关于这点,其实是在 C99 和 C++98 之前,C/C++ 是允许不显式指定返回值类型的,此时默认返回值类型是 int,但现在早就已经不行了。
为数据分配内存
C# 的类是引用类型,因此必须通过 new 关键字来分配内存。与 C++ 的有一点不同是,C++ 使用 new 创建对象时,是可以不加括号的,此时如果类定义了默认构造函数或者有虚函数,就会和加了括号一样,调用默认构造函数,如果只有编译器创建的默认构造函数且没有虚函数的话,是不会调用默认构造函数的。而 C# 使用 new 时必须加括号。
class Dealer{...}
class Program
{
static void Main()
{
// Dealer theDealer = new Dealer(); 也可以直接初始化
Dealer theDealer;
theDealer = new Dealer();
...
}
}
实例成员
C++ 经常会把类创建对象的过程叫实例化,但貌似挺少直接把类的具体对象叫实例的?
实例成员等同于 C++ 对象上的非静态成员变量,因为这些实例成员是每个实例自己的,与之相对的就是静态成员,参考 C++ 的静态成员变量,是每个类一个,所有实例共享的。
访问修饰符
C# 的访问修饰符和 Java 一样是直接写在变量或者方法名前面的,而不是 C++ 那种块作用域的方式
字段
访问修饰符 类型 标识符;
方法
访问修饰符 返回类型 方法名() {...}
C# 的访问修饰符比 C++ 要多,有 5 个:
- 私有的 (private)
- 公有的 (public)
- 受保护的 (protected)
- 内部的 (internal)
- 受保护内部的 (protected internal)
私有访问和公有访问
和 C++ 类似,类默认为私有访问,私有成员无法被其他对象访问
从类的内外访问成员
基本和 C++ 相同,只不过因为没有指针和普通对象的区别,所以只有 .
的方式,没有->
class DaysTemp
{
public int High, Low;
public int Average()
{
return (High + Low) / 2;
}
}
class Program
{
static void Main()
{
DaysTemp t1 = new DaysTemp();
DaysTemp t2 = new DaysTemp();
t1.High = 76;
t1.Low = 57;
t2.High = 75;
t2.Low = 53;
Console.WriteLine($"t1: {t1.High}, {t1.Low}, {t1.Average()}");
Console.WriteLine("t2: {0}, {1}, {2}", t2.High, t2.Low, t2.Average());
}
}
// t1: 76, 57, 66
// t2: 75, 53, 64