Skip to content

title: C# 版本历史 permalink: 1771337134.html sidebar: auto blogs: .NET date: '2026-02-17'

C# 版本历史

C# 1.0

发布时间:2002 年 1 月

类(class)是 C# 中用于定义对象的模板,它包含数据和行为,是面向对象编程的核心。

什么是类(Class)

在 .NET / C# 中:

类(class)是一种用于定义对象的类型。

简单理解:

  • 类 = 模板 / 蓝图
  • 对象 = 根据模板创建的实例

例如:

csharp
// [access modifier] - [class] - [identifier]
public class Person
{
    // Fields, properties, methods and events go here...
}

意思是定义了一个 Person 类型

类是引用类型

在 C# 中:

class 是引用类型(Reference Type)。

当你创建对象时:

csharp
Person p1 = new Person();

发生的事情:

  1. 堆内存 创建对象
  2. 变量 p1 只保存 对象地址(引用)

例如:

csharp
Person p1 = new Person();
Person p2 = p1;

现在:

p1 ----\
        > 指向同一个对象
p2 ----/

修改 p1 的数据,p2 也会看到变化。

创建对象

使用 new 关键字创建对象:

csharp
Person p = new Person();

或者简写:

csharp
Person p = new();

对象就是 类的实例

构造函数(Constructor)

构造函数在 创建对象时执行

例子:

csharp
// [access modifier] - [class] - [identifier]
public class Person
{
    // Fields, properties, methods and events go here...
    private int age;

    public Person()
    {
    }

    public Person(int age)
    {
        this.age = age;
    }
}

使用:

csharp
Person p = new Person(25);

构造函数通常用于初始化字段和属性。

对象初始化方式

C# 中对象初始化有几种方式:

默认值

例如:

  • int 默认 0
  • 引用类型默认 null
字段初始化
csharp
private int age = 25;
构造函数
csharp
public Person(int age)
{
    this.age = age;
}

继承(Inheritance)

C# 类支持 继承

csharp
class Student : Person
{
}

意思:

Person (父类)

   Student (子类)

子类可以:

  • 继承父类的属性
  • 继承方法
  • 扩展功能

注意:

C# 只能单继承(一个类只能继承一个父类)。

特殊类类型

抽象类
csharp
abstract class Animal
{
    public abstract void Speak();
}

特点:

  • 不能直接创建对象
  • 必须由子类实现方法
密封类
csharp
sealed class Dog
{
}

特点:

  • 不能被继承

结构

什么是 struct

struct(结构)是一种 值类型(Value Type),可以用来 封装数据和相关方法

简单理解:

  • struct = 一个 小型的数据对象
  • 用来存放 少量数据 + 少量逻辑

例如:

csharp
struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
}

使用:

csharp
Point p = new Point();
p.X = 10;
p.Y = 20;

structclass 最大区别

在 C# 里最重要的区别是 值类型 vs 引用类型

特性structclass
类型值类型引用类型
存储变量直接存数据变量存对象引用
赋值复制整个值复制引用
是否可继承不支持继承支持继承

例子:

csharp
struct Point
{
    public int X;
}
csharp
Point p = new Point();
p.X = 10;
Point p2 = p; // This creates a copy of p
p2.X = 100; // Modifying p2 does not affect p
Console.WriteLine(p.X); // Output: 10
Console.WriteLine(p2.X); // Output: 100

因为 struct 复制的是数据本身

如果是 class

csharp
class Point
{
    public int X;
}

输出会是 100,因为两个变量指向同一个对象。

什么时候用 struct

适合 小型数据类型

例如:

  • 坐标 Point
  • 颜色 Color
  • 时间 DateTime
  • 数值类型 int / double / bool

特点:

  • 数据量小
  • 不需要继承
  • 更像一个“数据容器”

struct 的默认值

所有字段都会自动初始化为 默认值

类型默认值
int0
boolfalse
引用类型null

例如:

csharp
Point point = default;
Console.WriteLine(point.X); // Output: 0

readonly struct

可以让结构 不可修改(immutable)

csharp
readonly struct ReadonlyPoint
{
    public int X { get; }
    // public int y; // This would cause a compile-time error because readonly structs cannot have mutable fields

    public ReadonlyPoint(int x)
    {
        X = x;
    }
}

好处:

  • 更安全
  • 性能更好(编译器可优化)

struct 的限制

struct 没有 class 灵活:

  1. 不能继承类或结构,但可以实现接口
csharp
struct MyStruct : IComparable
{
    public int CompareTo(object? obj) => 0;
}
  1. 不能定义析构函数

  2. 构造函数必须初始化所有字段

csharp
struct Point
{
    public int X;
    public int Y;

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
}

接口

什么是接口(Interface)

接口 = 行为的约定(合同 / 规范)

接口只规定 要做什么,但不规定 怎么做。 实现接口的类必须提供具体实现。

简单理解:

类型作用
接口定义规则
实现规则

为什么需要接口

因为 C# 不支持类的多继承。 但是一个类可以实现多个接口。

例如:

csharp
class Bird : IFlyable, IWalkable
{
    public void Fly()
    {
        Console.WriteLine("Bird fly");
    }

    public void Walk()
    {
        Console.WriteLine("Bird walk");
    }
}

含义:

Bird 同时拥有

  • 飞行能力
  • 行走能力

接口的定义

接口使用 interface 关键字。

示例:

csharp
interface IAnimal
{
    void MakeSound();
}

特点:

  • 只有方法声明
  • 没有方法体
  • 只是规定功能

实现接口

类实现接口必须实现里面的所有成员。

csharp
interface IAnimal
{
    void MakeSound();
}

class Dog : IAnimal
{
    public void MakeSound()
    {
        Console.WriteLine("Woof!");
    }
}

调用:

csharp
IAnimal dog = new Dog();
dog.MakeSound();

接口的特点

接口有几个重要规则:

接口不能创建对象

错误:

csharp
IAnimal animal = new IAnimal(); // This will cause a compile-time error because interfaces cannot be instantiated

必须用实现类:

csharp
IAnimal anotherDog = new Dog(); // This is valid because we are instantiating a class that implements the interface
类可以实现多个接口
csharp
class Bird : IFlyable, IWalkable
{
    public void Fly()
    {
        Console.WriteLine("Bird fly");
    }

    public void Walk()
    {
        Console.WriteLine("Bird walk");
    }
}
  • 类只能继承 一个类
  • 可以实现 多个接口
接口成员默认是 public

接口里的方法默认是:

public abstract

写成:

csharp
void Run();

等价于:

csharp
public abstract void Run();
接口不能有字段

接口 不能包含

  • 字段
  • 构造函数
  • 实例数据

可以包含:

  • 方法
  • 属性
  • 事件
  • 索引器

接口命名规范

按照约定,接口名称以大写字母 I 开头

例如:

IDisposable
IEnumerable
IComparable
IAnimal

接口的经典用途

最常见的是 解耦(降低耦合)

例子:

csharp
interface ILogger
{
    void Log(string message);
}

不同实现:

csharp
class FileLogger : ILogger
class DatabaseLogger : ILogger
class ConsoleLogger : ILogger

程序只依赖接口:

csharp
void Process(ILogger logger)
{
    logger.Log("Start");
}

好处:

  • 可以随时替换实现
  • 更容易测试
  • 更容易扩展

接口 vs 抽象类(简单区别)

对比接口抽象类
是否可多实现
是否有字段
是否有实现C# 8.0 开始可以有默认实现可以有非抽象方法
继承数量多个只能一个

属性

属性 = 用 get / set 控制字段访问的一种成员

主要目的:

  • 封装数据
  • 控制访问
  • 提供验证逻辑

什么是属性

在 C# 中,属性(Property) 是类或结构中的成员,用于 读取或设置对象的数据

可以把它理解为:

  • 字段(field)的安全访问方式
  • 通过 getset 控制数据读写

属性是字段的扩展,它本身 不直接表示存储位置,而是通过访问器控制读取和写入操作

简单理解:

字段 = 存数据
属性 = 控制如何读写数据

属性的基本语法

csharp
class Person
{
    private string name; // Backing field for the Name property

    public string Name // Property to access the name field
    {
        get { return name; }
        set { name = value; }
    }
}

说明:

  • get:读取属性时执行
  • set:给属性赋值时执行
  • valueset 里表示传入的新值

使用:

csharp
Person person = new Person();
person.Name = "Alice"; // Using the setter to set the name
Console.WriteLine(person.Name); // Using the getter to retrieve and print the name

自动实现的属性(最常用)

如果只是简单读写,不需要额外逻辑,可以用 自动属性

csharp
class Person
{
    public string Age { get; set; } // Auto-implemented property for Age
}

编译器会自动生成一个 隐藏字段(backing field)

使用:

csharp
Person p = new Person();
person.Age = "30"; // Using the auto-implemented property to set the age
Console.WriteLine(person.Age); // Using the auto-implemented property to retrieve and print

只读 / 只写属性

只读属性
csharp
public string Name { get; }

或者

csharp
public string Name
{
    get { return name; }
}
只写属性(较少用)
csharp
public string Name
{
    set { name = value; }
}

属性访问修饰符

属性的 getset 可以有不同权限。

csharp
public string Name { get; private set; }

意思:

外部可以读取
只有类内部可以修改

常见写法:

public string Name { get; private set; }
public int Age { get; protected set; }

带逻辑的属性

属性可以加入验证逻辑。

csharp
private int age;

public int Age
{
    get { return age; }
    set
    {
        if (value >= 0)
            age = value;
    }
}

作用:

  • 防止非法数据
  • 控制赋值规则

计算属性

属性不一定需要字段,可以 动态计算

csharp
public int Age
{
    get { return DateTime.Now.Year - birthYear; }
}

表达式属性(简写)

C# 支持更简洁写法:

csharp
public int Age => DateTime.Now.Year - birthYear;

属性 vs 字段

特点字段属性
存储数据不一定
可以控制访问
推荐使用不推荐 public推荐

最佳实践:

字段 private
属性 public

示例:

csharp
private int age;
public int Age { get; set; }

委托

什么是委托

委托(delegate)是一种类型,表示具有特定参数列表和返回类型的方法引用。

委托可以把 方法当作参数传递或存储

可以把它理解为:C 语言中的 函数指针,但 类型安全 + 面向对象

为什么需要委托

委托主要解决 后期绑定(Late Binding)问题

意思是:

程序运行时才决定 调用哪个方法

例如,假设你有一个计算器可以对两个数字执行运算。 你可以使用委托来表示任何接受两个数字并返回结果的运算,而不是将加法、减法、乘法和除法硬编码到单独的方法中。

排序逻辑一样 但 比较方法不同

委托允许:

算法 + 可替换的方法

定义委托类型

使用 delegate 关键字定义。

csharp
// Define a delegate that takes two integers and returns an integer
public delegate int PerformCalculation(int a, int b);

含义:

PerformCalculation
表示一个方法类型:

参数:
int x
int y

返回值:
int

PerformCalculation 委托可以保留对任何采用两个 int 参数并返回 int 的方法的引用。

使用委托

定义方法
csharp
// Define a method that matches the signature of the delegate
static int Add(int a, int b)
{
    return a + b;
}
声明委托的实例
csharp
// Create an instance of the delegate and assign it to the Add method
PerformCalculation calculation = Add;
调用委托
csharp
// Invoke the delegate and print the result
int result = calculation(5, 3);
Console.WriteLine(result);

委托底层原理

当你写:

csharp
public delegate int PerformCalculation(int x, int y);

编译器会自动生成一个类:

class PerformCalculation : MulticastDelegate

继承关系:

System.Object

System.Delegate

System.MulticastDelegate

PerformCalculation (编译器生成)

所有 C# 委托本质都是 MulticastDelegate

Delegate 和 MulticastDelegate 类

System.Delegate 类及其直接子类 System.MulticastDelegate 为创建委托、将方法注册为委托目标以及调用向委托注册的所有方法提供框架支持。

下面是一个有趣的设计详细信息:System.DelegateSystem.MulticastDelegate 并不是你可以使用的委托类型。 相反,它们充当所创建的所有特定委托类型的基类。 C# 语言会阻止你直接从这些类继承—你必须改用 delegate 关键字。

使用 delegate 关键字声明委托类型时,C# 编译器会根据您的特定签名,自动创建一个从 MulticastDelegate 派生的类。

使用委托方法

在使用委托时,最常用的方法是:

Invoke():调用附加到委托上的所有方法。

BeginInvoke() / EndInvoke():用于异步调用模式(不过现在更推荐使用 async/await)。

多播委托

多播委托是指:

一个委托实例可以同时引用并调用多个方法。

当调用这个委托时,它会按顺序依次调用所有绑定的方法。这是 C# 委托非常重要的特性之一。

  • 添加和移除方法
运算符作用
+=添加方法
-=移除方法
  • 调用列表

GetInvocationList()

  • 调用顺序

执行顺序是添加顺序

  • 返回值

返回值只会是最后一个方法的返回值

  • 异常

后续方法不再调用

示例代码
  1. 定义委托
csharp
public delegate int MyDelegate();
  1. 定义方法
csharp
static int Method1()
{
    Console.WriteLine("Method1 called");
    return 1;
}
static int Method2()
{
    Console.WriteLine("Method2 called");
    return 2;
}
static int Method3()
{
    Console.WriteLine("Method3 called");
    return 3;
}
  1. 创建多播委托
csharp
// Create a multicast delegate and add multiple methods to it
MyDelegate multiCastDelegate = Method1;
multiCastDelegate += Method2;
multiCastDelegate += Method3;

// List the methods in the multicast delegate
multiCastDelegate.GetInvocationList().ToList().ForEach(d => Console.WriteLine($"Method: {d.Method.Name}"));

// Invoke the multicast delegate and print the result
int multiCastResult = multiCastDelegate();
Console.WriteLine(multiCastResult);

输出:

Method: Method1
Method: Method2
Method: Method3
Method1 called
Method2 called
Method3 called
3

内置委托

C# 中,内置委托 是 .NET 已经提供好的通用委托类型,你通常 不需要自己再定义 delegate

最常用的三个是:

Action
Func
Predicate
Action 委托

Action 表示:没有返回值的方法。

特点:

返回值:void
参数:0~16 个
csharp
Action<string> print = Console.WriteLine;

print("Hello");

等价于:

csharp
delegate void Print(string s);
常见 Action 类型
类型含义
Action无参数
Action<T>1个参数
Action<T1,T2>2个参数
Action<T1,...T16>最多16个参数

示例:

csharp
Action<int, int> add = (a, b) =>
{
    Console.WriteLine(a + b);
};

add(3,5);
Func 委托

Func 表示:有返回值的方法。

特点:

最后一个类型参数 = 返回值类型
csharp
Func<int, int, int> add = (a, b) => a + b;

int result = add(3,5);

解释:

Func<int,int,int>

前两个 int → 参数
最后一个 int → 返回值

等价写法:

csharp
delegate int Add(int a, int b);
常见 Func 形式
类型含义
Func<TResult>无参数有返回值
Func<T,TResult>1参数
Func<T1,T2,TResult>2参数
Func<...>最多16参数

示例:

csharp
Func<int, int> square = x => x * x;
Predicate 委托

Predicate 表示:返回 bool 的判断方法。

定义:

csharp
Predicate<T>

等价于:

csharp
delegate bool Predicate<T>(T obj);
csharp
Predicate<int> isEven = x => x % 2 == 0;

Console.WriteLine(isEven(4));

输出:

True
常见用途

例如:

csharp
List<int>.Find
List<T>.RemoveAll
List<T>.Exists

示例:

csharp
List<int> numbers = new List<int> {1,2,3,4,5};

numbers.RemoveAll(x => x % 2 == 0);
三个内置委托对比
委托返回值用途
Actionvoid执行动作
Func任意类型计算
Predicatebool判断

简单记忆:

Action    → 做事情
Func      → 计算结果
Predicate → 条件判断

事件

什么是事件(Event)

事件 = 对象之间的“通知机制”

事件允许一个类在“某件事发生时”通知其他类 ([Microsoft Learn][1])

事件核心角色

事件模型本质是 发布-订阅模式(Observer)

角色说明
发布者(Publisher)定义事件并触发事件的对象
订阅者(Subscriber)订阅事件并在事件发生时接收通知的对象
  • 发布者决定 什么时候触发
  • 订阅者决定 做什么处理

事件的本质

事件其实是 基于委托(delegate)封装的

  • event 是对委托的进一步限制和封装
  • 外部只能订阅(+=)和取消订阅(-=),不能直接触发事件(更安全)
event = 更安全的 delegate

事件的基本用法

  1. 定义委托(Delegate):首先需要定义一个委托,该委托指定了事件处理方法的签名(即参数和返回类型)。
cs
// Define a delegate for the event handler
public delegate void MyEventHandler(string message);
  1. 声明事件(Event):在发布者类中,使用event关键字声明一个事件,该事件基于之前定义的委托。

  2. 触发事件:在发布者类中,编写触发事件的代码。通常,在某个条件满足时,调用事件(即调用委托)。

cs
// Define a class that publishes the event
class MyEventPublisher
{
    // Declare the event using the delegate
    public event MyEventHandler MyEvent;

    public void TriggerEvent(string message)
    {
        // Check if there are any subscribers to the event
        if (MyEvent != null)
        {
            MyEvent(message); // Raise the event
        }
    }
}
  1. 订阅事件:在订阅者类中,创建与事件委托签名匹配的方法,并将该方法添加到事件的订阅列表中(使用+=操作符)。
cs
// Define a class that subscribes to the event
class MyEventSubscriber
{
    public void OnMyEvent(string message)
    {
        Console.WriteLine("Event received: " + message);
    }
}
cs
MyEventPublisher publisher = new MyEventPublisher();
MyEventSubscriber subscriber = new MyEventSubscriber();
// Subscribe to the event
publisher.MyEvent += subscriber.OnMyEvent;
// Trigger the event
publisher.TriggerEvent("Hello, world!");
  1. 取消订阅(可选):使用-=操作符可以将方法从事件的订阅列表中移除。
cs
// Unsubscribe from the event
publisher.MyEvent -= subscriber.OnMyEvent;
// Trigger the event again (no output expected)
publisher.TriggerEvent("This will not be received.");

事件与委托的区别

事件是委托的封装,事件内部基于委托实现,但事件提供了更好的封装性。

事件只能在声明它的类内部触发(即只能由发布者触发),而委托可以在任何地方被调用。

事件只能通过+=-=来添加或移除事件处理程序,而委托可以使用赋值操作(=)和+-等操作。

对比委托事件
本质方法引用委托封装
调用权限任意调用只能类内部触发
安全性
使用场景回调发布-订阅

标准事件模式

一种“发布-订阅(Observer)”机制的标准写法

  • 发布者(Publisher):触发事件
  • 订阅者(Subscriber):接收并处理事件

好处:

  • 解耦(发布者不知道谁在监听)
  • 支持多个监听者
  • 易扩展
事件委托签名
csharp
void EventRaised(object sender, EventArgs args);
参数作用
sender谁触发了事件
args事件数据
  • 返回值必须是 void
  • sender 一律用 object
  • args 必须是 EventArgs 或其子类
标准写法
cs
// Alternative using EventHandler<T> for more complex event data
class MessageEventArgs : EventArgs
{
    public string Message { get; }

    public MessageEventArgs(string message)
    {
        Message = message;
    }
}

class MyEventPublisherWithArgs
{
    // Declare the event using EventHandler<T>
    public event EventHandler<MessageEventArgs>? MyEvent;

    public void TriggerEvent(string message)
    {
        MyEvent?.Invoke(this, new MessageEventArgs(message)); // Raise the event with arguments
    }
}

class MyEventSubscriberWithArgs
{
    public void OnMyEvent(object sender, MessageEventArgs e)
    {
        Console.WriteLine("Event received: " + e.Message);
        Console.WriteLine("Event sender: " + sender.GetType().Name);
    }
}

// Using the alternative with EventHandler<T>
MyEventPublisherWithArgs publisherWithArgs = new MyEventPublisherWithArgs();
MyEventSubscriberWithArgs subscriberWithArgs = new MyEventSubscriberWithArgs();
// Subscribe to the event
publisherWithArgs.MyEvent += subscriberWithArgs.OnMyEvent;
// Trigger the event with arguments
publisherWithArgs.TriggerEvent("Hello, world with arguments!");

现代事件模式

  1. 不再强制继承 EventArgs

旧模式:

csharp
class MyArgs : EventArgs { }

新模式:

csharp
class MyArgs { }   // 可以不继承 EventArgs

关键点:

  • EventHandler<T> 不再要求 T : EventArgs

原因:

  • EventArgs本身几乎没提供实际功能
  • 反而限制设计(比如不能用 struct
  1. 事件参数可以是 struct(值类型)
csharp
internal struct SearchDirectoryArgs
{
    public string CurrentSearchDirectory { get; }
    public int TotalDirs { get; }
    public int CompletedDirs { get; }
}

优点:

  • 减少堆分配(性能更好)

注意:

  • 不适合需要“修改共享数据”的场景(如取消操作)
异步事件

问题:事件 + async 很尴尬

事件签名必须是:

csharp
void Handler(object sender, EventArgs e)

但 async 推荐:

csharp
Task MethodAsync()

冲突点:

  • 事件只能用 void
  • async void 是危险的(异常无法传播)

正确写法

csharp
worker.StartWorking += async (sender, args) =>
{
    try
    {
        await DoWorkAsync();
    }
    catch (Exception e)
    {
        Console.WriteLine($"Async task failure: {e}");
    }
};

关键规则:

  1. 必须 async void
  2. 必须 try-catch 包裹 await

为什么必须捕获异常?

因为:

  • async void 无法返回 Task

  • 异常不会被调用方捕获

  • 可能:

    • 崩溃线程
    • 崩溃进程
    • 状态混乱

标准事件和现代事件的区别

特性传统事件现代事件
EventArgs 继承必须不需要
参数类型classclass / struct
灵活性较低更高
async 支持不明确有明确模式
兼容性完全兼容

运算符和表达式

什么是 C# 运算符

C# 中,运算符(Operator)就是对数据进行操作的符号,比如:

csharp
+  -  *  /  ==  &&  ??  ?:

什么是 C# 表达式

表达式本质是:

操作数 + 运算符 → 结果

例如:

csharp
a + b
x > y

运算符的三大类型

C# 运算符按“操作数数量”分:

一元运算符(1个操作数)
csharp
-x    ++x    x++    !x
二元运算符(2个操作数)
csharp
a + b
a == b
a && b
三元运算符(唯一)
csharp
condition ? x : y

常见运算符分类

算术运算符
csharp
+  -  *  /  %
比较运算符
csharp
>  <  >=  <=
相等运算符
csharp
==  !=
逻辑运算符
csharp
&&  ||  !
位运算符
csharp
&  |  ^  <<  >>
赋值运算符
csharp
=  +=  -=  *=  /=  ??=
其他重要运算符
csharp
??     // 空合并
?.     // 空条件访问
is     // 类型判断
as     // 类型转换
new    // 创建对象

运算符优先级

结论:乘除 > 加减

csharp
var a = 2 + 2 * 2;   // = 6
var b = (2 + 2) * 2; // = 8

不会就加括号,永远安全

结合性(运算顺序)

当优先级一样时:

  • 左结合(默认)
csharp
a - b - c  // (a - b) - c
  • 右结合(少数)
csharp
a = b = c  // a = (b = c)

右结合的有:

  • 赋值 =
  • 三元 ?:
  • null 合并 ??

语句

什么是“语句”(Statement)

在 C# 中:

语句 = 程序执行的最基本单位(“做一件事”)

比如:

csharp
int a = 10;      // 声明 + 赋值
a++;             // 自增
Console.WriteLine(a); // 调用方法

这些每一行就是一个语句。

语句的基本形式

单行语句
csharp
int x = 5;

必须以 ; 结尾

语句块(多行)
csharp
{
    int x = 5;
    x++;
}

{} 包裹,可以嵌套

语句的主要分类

声明语句(Declaration)

创建变量 / 常量

csharp
int a;
int b = 10;
const double PI = 3.14;

常量必须初始化

表达式语句(Expression)

做计算或操作

csharp
a = 5;
a++;
Console.WriteLine(a);

注意:

csharp
a + 1;   // ❌ 不合法(没有意义)
选择语句(分支)

条件判断

csharp
if (a > 0) { }

switch (a) { }
迭代语句(循环)
csharp
for (...) { }
while (...) { }
foreach (...) { }
do { } while (...);

重复执行代码

跳转语句(控制流程)
csharp
break;
continue;
return;
goto;

改变执行路径

异常处理语句
csharp
try { }
catch { }
finally { }
throw;

处理运行时错误

其他特殊语句
  • lock(线程锁)
  • checked / unchecked(溢出检查)
  • yield return(迭代器)
  • await(异步)
空语句
csharp
;

什么都不做,但语法上需要

示例:

csharp
while(condition);

嵌入语句

例如:

csharp
if (true)
    int a = 5;   // 错误

原因: 单行嵌入语句不能是声明语句

正确写法:

csharp
if (true)
{
    int a = 5;
}

控制流

语句执行顺序 = 控制流

特点:

  • 默认:从上到下

  • 可以被改变:

    • if(分支)
    • 循环
    • return / break

每次运行可能不同(取决于输入)

反射与特性

什么是“特性(Attribute)”

一句话:给代码贴“标签”(元数据)

特性本质是:

给类、方法、属性等附加“描述信息”,这些信息不会直接执行,而是供程序读取使用

  • 示例
csharp
[Serializable]
public class User
{
}

表示:这个类“可以被序列化”

特性特点
  • 本质是 元数据(metadata)

  • 可以加在:

    • 类 / 方法 / 属性 / 参数 / 程序集 等
  • 可以带参数

  • 本身不会自动执行逻辑

特性只是“数据”,真正用它的是别的代码(比如框架)

什么是“反射(Reflection)”

一句话:程序在运行时“查看自己”

反射可以在运行时获取类型信息、创建对象、调用方法等

反射常见能力

反射可以做到:

  • 获取类型
csharp
int x = 10;
var type = x.GetType();
Console.WriteLine(type.FullName); // Output: System.Int32
  • 获取程序集
csharp
Assembly assembly = typeof(int).Assembly;
Console.WriteLine(assembly.FullName); // Output: System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
  • 动态调用方法
  • 读取属性/字段
  • 创建对象(不写 new)

示例代码

定义特性
csharp
public class AuthorAttribute : Attribute
{
    public string Name;
    public AuthorAttribute(string name)
    {
        Name = name;
    }
}
使用特性
csharp
[Author("Panda")]
public class SampleClass{}
用反射读取
csharp
// Get custom attributes of SampleClass
Type sampleType = typeof(SampleClass);
AuthorAttribute authorAttribute = sampleType.GetCustomAttribute<AuthorAttribute>();
if (authorAttribute != null)
{
    Console.WriteLine(authorAttribute.Name); // Output: Panda
}

Last updated:

Released under the MIT License.