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 Programstatic 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 类中有 WriteWriteLine,前者不会自动加换行,后者会自动加换行

格式化字符串

类似 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

image-20250501185714613

注释

单行和多行注释和 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
      • 浮点类型
        • decimal (高精度浮点数,占 16 个字节)
        • float
        • double
  • object(所有其他类型的基类)
  • string
  • dynamic(使用动态语言编写的程序集时使用)

用户定义类型

除此之外有 6 种类型可以由用户创建

  • 类类型 (class)
  • 结构类型 (struct)
  • 数组类型 (array)
  • 枚举类型 (enum)
  • 委托类型 (delegate)
  • 接口类型 (interface)

栈和堆

栈和 C++ 的区别不大,堆的话因为 C# 有垃圾回收,所以不需要显式也不能显式的删除堆里的数据,因此也没有 delete 关键字

值类型和引用类型

C# 的引用类型更接近于 C++ 的指针而不是引用,只不过没有 * 号,引用类型存储的实际数据总是位于堆上,而引用也就是地址部分可在堆上也可在栈上,且引用类型可以为 null

预定义类型中,三个非简单类型都是引用类型,简单类型里只有三个浮点类型和 char 是引用类型

用户定义类型中只有 structenum 是值类型,其他都是引用类型

变量

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