C# 语言笔记

本文档结合《C#语言入门讲解》和《C# 12.0 本质论》提炼C#相关知识

学习资料:

前言/基础概念

心法

  • 不要怕见到自己看不懂的东西
  • 要跟着操作,一遍遍练习,熟悉手里的东西

程序

程序的编写流程: 编辑 编译 调试 发布

编程的学习路径

  • 纵向:语言、类库、框架
  • 横向:语言的各种应用,命令行、桌面应用、移动端、Web、游戏…

作业:

用 WPF 完成一个 Hello World 程序,点击按钮之后,TextBox 显示 “Hello World!”

项目

Solution 与 Project

  • Solution是总的解决方案
  • Project是解决具体的某个问题
    • Console
    • WPF
    • Windows Forms

各种 Hello World

  • Console Application
  • WPF
  • Windows Forms

基本元素

  • 关键字
  • 操作符(逻辑与或非等等)
  • 标识符(名字)
    • 命名方法
      • Pascal 法(C#常用)
      • 驼峰法 thisIsAVariable(Java常用)
  • 标点符号
  • 文本(字面值)
    • 整数:int, long(3L, 64bit)
    • 实数:float(3f), double(3D)
    • 字符:单引号,一个字符
    • 字符串:双引号
    • 布尔:
    • 空 Null
  • 注释与空白
    • 单行 //
    • 多行 /**/
    • VSCode 块注释快捷键:Shift + Alt + A(Windows/Linux),Option + Shift + A(Mac)
    • VSCode 格式化快捷键:Shift + Alt + F(Windows/Linux),Option + Shift + F(Mac)

类与对象

对象与实例

简单理解,对象就是实例。

精确理解,在编程语境下,对象通常指现实世界中的物体,实例通常指类(代码)实例化出来的一个Object(对象)。

静态成员与非静态成员

核心区别是,这两个概念,成员属于“类”还是具体的“实例”

public class NewDreamer
{
    private string _fullName = "黎恩瑜";
    public NewDreamer(string nickName)
    {
        this._fullName = nickName;
    }
    public string Introduce() => $"大家好,我是{_fullName}";
}

类型变量与方法

数据类型:int、float、double…

方法与函数:

在 C 语言中叫做函数,后来发展为 C++,函数成为了类成员,后面遍开始叫做方法(成员函数)。

算法:循环与递归

序列化与反序列化

序列化:将对象转化为可以存储在内存中/网络传输的格式的过程,比如转化成XML文件、JSON文件、二进制文件等等 反序列化:用文件的内容重建为对象

语法

一些初始化声明

平常写代码的时候,声明变量完了,编辑器总是提示我这里可以 Quick Fix、那里也可以,导致我总是写着写着怀疑人生,搞得都不知道该如何声明变量了,所以在这里总结了一下。

C# 中有几种变量,基本类型、对象类型、集合类型等等。

// 基本类型 - 有默认值
int number = 0;           // 或者 int number; (默认0)
bool flag = false;        // 或者 bool flag; (默认false)
string text = "";         // 或者 string text; (默认null)
char ch = '\0';           // 或者 char ch; (默认'\0')
 
/* 对象类型 */
 
// 传统方式
Person person = new Person();
Person person2 = new Person("John", 25);
// C# 9.0+ 目标类型推断
Person person3 = new("John", 25);  // 编译器推断类型
var person4 = new Person("John", 25);
 
/* 集合类型 */
 
// 传统方式
List<int> numbers = new List<int>();
List<int> numbers2 = new List<int> { 1, 2, 3, 4 };
// C# 9.0+ 目标类型推断
List<int> numbers3 = new() { 1, 2, 3, 4 };
var numbers4 = new List<int> { 1, 2, 3, 4 };
// C# 12+ 集合表达式
List<int> numbers5 = [1, 2, 3, 4];  // 最新语法
 

主要区别在于集合类型和对象类型在不同版本标准的 C# 中的语法不同。

对于集合类型,传统方式需要在 new 后面写出类型的类名、参数;C# 9.0 之后可以省略类名和参数,声明的内容(键值对)依旧使用 {} 花括号来表示(对象初始化器语法);C# 12.0 之后,开始使用赋值初始化(索引器初始化器语法)

字典初始化方式变化:

// 传统方式
 
Dictionary<string, int> dict = new Dictionary<string, int>();
Dictionary<string, int> dict2 = new Dictionary<string, int>
{
    {"apple", 1},
    {"banana", 2}
};
 
// C# 9.0+ 目标类型推断
Dictionary<string, int> dict3 = new()
{
    {"apple", 1},
    {"banana", 2}
};
 
// C# 12+ 集合表达式
Dictionary<string, int> dict4 = new()
{
    ["apple"] = 1,
    ["banana"] = 2
};

集合初始化方式变化:

// 传统方式
List<int> numbers = new List<int>();
List<int> numbers2 = new List<int> { 1, 2, 3, 4 };
  
// C# 9.0+ 目标类型推断
List<int> numbers3 = new() { 1, 2, 3, 4 };
var numbers4 = new List<int> { 1, 2, 3, 4 };
  
// C# 12+ 集合表达式
List<int> numbers5 = [1, 2, 3, 4];  // 最新语法

条件判断:If()

短路求值及一些 Trick 写法

在 C# 中,逻辑运算符&&(逻辑与)和||(逻辑或)具有短路求值特性,这意味着当第一个操作数已经能确定整个表达式的结果时,第二个操作数不会被执行。

判空操作:避免空指针

// 判断 a、b 数组是否为空且长度是否相等,先进行判空操作,避免 a 或 b 为 null 的时候访问成员变量 Length 的时候报 NullReferenceException
if (a == null || b == null || a.Length != b.Length)
	return false;

条件执行函数 & 条件赋值 & 替代TryCatch 条件执行函数、替代TryCatch

isReady && DoSomething();
// 等价于
if (isReady) DoSomething();

条件赋值

// 在 input 不为空且不为空字符串的时候才赋值,否则为 Default
string name = input != null && input.Length > 0 ? input : "Default";
 
// user 不为空才赋值,但布尔表达式需要 `&&` 两边都是布尔值,所以右边需要再判断一下 != null
(user != null && (user.Name = "Leo") != null);

循环提前结束

可以不用写 break;

// 角色向目标点移动的逻辑(每帧更新)
void MoveToTarget(Character character, Vector3 target)
{
    // 短路逻辑:
    // 1. 先判断角色是否有效(未销毁)
    // 2. 再判断是否到达目标点(未到达则继续循环)
    // 3. 最后执行移动逻辑(只有前两个条件都满足才执行)
    // MoveToWards 未在这里实现
    while (character != null 
           && !IsReachedTarget(character, target) 
           && character.MoveTowards(target, Time.deltaTime)) 
    {
        // 循环体为空,所有逻辑在条件中通过短路求值完成
        // 每帧等待一次更新(避免死循环阻塞主线程)
        yield return null; 
    }
 
    // 移动结束后的处理
    if (character != null)
    {
        character.PlayIdleAnimation();
    }
}
 
// 检测是否到达目标点
bool IsReachedTarget(Character character, Vector3 target)
{
    return Vector3.Distance(character.Position, target) < 0.1f;
}

Char

char.GetNumericValue(x)

获取字符 x 的数值

String

String 和 string

两者使用上没什么区别,一个是类型名称,一个是关键字,在代码中两个都可以使用。

string 是 C# 的关键字,不需要引用命名空间就可以使用(与 bool, int, char 保持一致,都是小写)。 String 是类型名称(.NET Framework 的类型),需要引用命名空间 using System 才可以直接使用。 string 在编译成 IL 语言之后,会被编译成 Sytem.String

string.Concat()

连接字符串

string.Join()

string.Join(string separator, params string[] value) 连接字符串,但是可以添加连接符

Array 数组

a.SequenceEqual(b) 判断两个序列是否相等

  • 要求两个序列的元素数量必须相同,否则直接返回 false
  • 要求每个位置的元素必须相等(按顺序一一对比),顺序不同则判定为不相等。
  • 对于值类型(如 intdouble),直接比较值是否相等。
  • 对于引用类型(如自定义类),默认比较引用地址,若需比较对象内容,需重写 Equals 方法或使用 IEqualityComparer<T> 自定义比较规则。

与 Equals 或 == 的区别:

  • 数组 / 列表的 Equals 或 == 比较的是引用是否相同(是否为同一个对象),而 SequenceEqual 比较的是内容是否相同
int[] arr1 = { 1, 2 };
int[] arr2 = { 1, 2 };
 
Console.WriteLine(arr1 == arr2); // false(引用不同) Console.WriteLine(arr1.SequenceEqual(arr2)); // true(内容相同)

Linq

LINQ操作汇总

过滤

OfType

// 将 listOfItems 中的 int 类型元素过滤出来
return listOfItems.OfType<int>();

Where

return listOfItems
.Where(x => x.GetType() == typeof(int))
.Select(x => (int)x);

强制类型转换

Cast

return listOfItems
.Where(x => x is int)
.Cast<int>();

Select

操作

排序

OrderBy/OrderByDescending,按一定规则升序/降序排序

// 对整数数组升序排序
int[] numbers = {3, 1, 4, 1, 5, 9, 2, 6};
var sorted = numbers.OrderBy(x => x).ToArray();
// 结果: [1, 1, 2, 3, 4, 5, 6, 9]
 
// 对字符串数组按长度排序
string[] words = {"apple", "pie", "banana", "cat"};
var byLength = words.OrderBy(x => x.Length).ToArray();
// 结果: ["pie", "cat", "apple", "banana"]

对象排序

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string City { get; set; }
}
var people = new List<Person>
{
    new Person { Name = "Alice", Age = 25, City = "Beijing" },
    new Person { Name = "Bob", Age = 30, City = "Shanghai" },
    new Person { Name = "Charlie", Age = 25, City = "Beijing" }
};
 
// 按年龄排序
var byAge = people.OrderBy(p => p.Age).ToList();
// 按姓名排序
var byName = people.OrderBy(p => p.Name).ToList();
// 按城市排序
var byCity = people.OrderBy(p => p.City).ToList();

ThenBy,可以继续排序

----- CodeWars -----

2025年7月27日 开始记录

刷好多题容易忘,还是开一篇文章记录一下

正则表达式

正则表达式采用的是 C# 中的 Regex 类,可以通过@运算符来设置 pattern 字符串,填写正则表达式,并通过 IsMatch 等函数来返回查询结果

在字符串大写字母前添加空格

public static string BreakCamelCase(string str)
=> Regex.Replace(str, "(?<!^)([A-Z])", " $1");
  • () 代表分组
  • [A-Z] 代表大写字母
  • ?< 之前,表示大写字母之前
  • !^ 感叹号表示“非”,^ 表示开头,所以 !^ 表示不能为开头
  • (?<!^) 大写字母之前不能是开头,这样不会匹配到一句话的开头
  • $1 空格、$ 表示结尾、 $1 表示空格后1个字符

字符串操作

删去首尾字符

s.Substring(startindex, length);

字符串范围表达

public static string Remove_char(string s) => s[1..^1];

数组操作

求平均数

public static double FindAverage(double[] array) => array.Length == 0 ? 0 : array.Average();

250727 汉诺塔&位运算

https://www.codewars.com/kata/534eb5ad704a49dcfa000ba6

解法:公式/位运算

位运算之左移运算:

x << n 表示把数字 x 的二进制表示向左移动 n 位

每向左移动一位,数字就相当于乘以 2

例如:

  • 1 << 0 = 1 (二进制 0001)
  • 1 << 1 = 2 (二进制 0010)
  • 1 << 2 = 4 (二进制 0100)
  • 1 << 3 = 8 (二进制 1000)
Console.WriteLine($"位运算2的3次方:{1<<3}");
位运算2的3次方:8