高级特性概览
反射(Reflection)
反射允许程序在运行时检查和操作自身的类型信息。Type 类是反射的核心,通过它可以获取字段、属性、方法等元数据。
获取类型信息
CSHARP
using System;
using System.Reflection;
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public void SayHello() => Console.WriteLine("Hello!");
}
class Program
{
static void Main()
{
Type type1 = typeof(Person);
Type type2 = new Person().GetType();
Console.WriteLine($"类型名: {type1.Name}");
Console.WriteLine("属性:");
foreach (PropertyInfo p in type1.GetProperties())
Console.WriteLine($" {p.Name} ({p.PropertyType.Name})");
Console.WriteLine("方法:");
foreach (MethodInfo m in type1.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
Console.WriteLine($" {m.Name}");
}
}
TEXT
类型名: Person
属性:
Name (String)
Age (Int32)
方法:
get_Name
set_Name
get_Age
set_Age
SayHello
动态创建实例
CSHARP
using System;
using System.Reflection;
class Dog
{
public string Breed { get; set; } = "Unknown";
public override string ToString() => $"Dog: {Breed}";
}
class Program
{
static void Main()
{
Type type = typeof(Dog);
object obj = Activator.CreateInstance(type)!;
PropertyInfo prop = type.GetProperty("Breed")!;
prop.SetValue(obj, "Husky");
Console.WriteLine(obj);
}
}
TEXT
Dog: Husky
示例
CSHARP
using System;
using System.Linq;
using System.Reflection;
class Calculator
{
public int Add(int a, int b) => a + b;
public double Multiply(double x, double y) => x * y;
public void Reset() { }
}
class Program
{
static void Main()
{
Calculator calc = new Calculator();
Type type = calc.GetType();
Console.WriteLine($"类型: {type.Name}");
Console.WriteLine($"命名空间: {type.Namespace}");
Console.WriteLine($"是否为类: {type.IsClass}");
Console.WriteLine("公共方法:");
MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
foreach (MethodInfo m in methods)
{
string parameters = string.Join(", ", m.GetParameters().Select(p => $"{p.ParameterType.Name} {p.Name}"));
Console.WriteLine($" {m.ReturnType.Name} {m.Name}({parameters})");
}
}
}
TEXT
类型: Calculator
命名空间:
是否为类: True
公共方法:
Int32 Add(Int32 a, Int32 b)
Double Multiply(Double x, Double y)
Void Reset()
Attribute 特性
特性是用于为代码元素添加元数据的声明性标签。可使用内置特性或自定义特性。
内置特性
CSHARP
using System;
class LegacyService
{
[Obsolete("请使用 NewService 代替", error: false)]
public void OldMethod() => Console.WriteLine("旧方法");
public void NewMethod() => Console.WriteLine("新方法");
}
class Program
{
static void Main()
{
var service = new LegacyService();
service.OldMethod();
service.NewMethod();
}
}
TEXT
旧方法
新方法
💡
Obsolete 的 error 参数设为 true 时,调用旧方法将产生编译错误而非警告。
自定义特性
CSHARP
using System;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
class DescriptionAttribute : Attribute
{
public string Text { get; }
public DescriptionAttribute(string text) => Text = text;
}
[Description("用户管理服务")]
class UserService
{
[Description("获取所有用户")]
public void GetAll() { }
}
class Program
{
static void Main()
{
var attr = Attribute.GetCustomAttribute(typeof(UserService), typeof(DescriptionAttribute)) as DescriptionAttribute;
Console.WriteLine(attr?.Text ?? "无描述");
}
}
TEXT
用户管理服务
泛型约束深入
泛型约束通过 where 关键字限制类型参数的范围,确保类型参数满足特定条件。
约束类型一览
| 约束 | 说明 |
|---|---|
where T : class |
T 必须是引用类型 |
where T : struct |
T 必须是值类型(不可为 null) |
where T : new() |
T 必须有无参公共构造函数 |
where T : ISpecific |
T 必须实现指定接口 |
where T : BaseClass |
T 必须继承指定基类 |
where T : unmanaged |
T 必须是非托管类型 |
where T : notnull |
T 必须是不可为 null 的类型 |
示例
CSHARP
using System;
interface IRepository
{
string GetName();
}
class SqlRepository : IRepository
{
public string GetName() => "SQL Repository";
}
class Service<T> where T : class, IRepository, new()
{
private T _repo = new T();
public void Print() => Console.WriteLine(_repo.GetName());
}
class Program
{
static void Main()
{
var service = new Service<SqlRepository>();
service.Print();
}
}
TEXT
SQL Repository
⚠️
new() 约束必须放在约束列表的最后,class 或 struct 应放在最前。
迭代器(yield)
迭代器使用 yield return 逐个生成序列元素,实现惰性求值——元素仅在迭代时才计算。
yield return 与 yield break
CSHARP
using System;
using System.Collections.Generic;
class Program
{
static IEnumerable<int> GetNumbers()
{
for (int i = 0; i < 5; i++)
{
if (i == 3) yield break;
yield return i;
}
}
static void Main()
{
foreach (int n in GetNumbers())
Console.WriteLine(n);
}
}
TEXT
0
1
2
💡
yield break 立即终止迭代。迭代器方法返回 IEnumerable<T> 或 IEnumerator<T>。
示例
CSHARP
using System;
using System.Collections.Generic;
class Program
{
static IEnumerable<int> Fibonacci(int count)
{
int a = 0;
int b = 1;
for (int i = 0; i < count; i++)
{
yield return a;
int temp = a + b;
a = b;
b = temp;
}
}
static void Main()
{
Console.WriteLine("斐波那契数列前10项:");
foreach (int n in Fibonacci(10))
{
Console.Write(n + " ");
}
Console.WriteLine();
}
}
TEXT
斐波那契数列前10项:
0 1 1 2 3 5 8 13 21 34
Span<T> 与 Memory<T>
Span<T> 和 Memory<T> 提供对连续内存区域的无拷贝切片访问,是高性能场景的关键类型。
Span<T> 基本用法
CSHARP
using System;
class Program
{
static void Main()
{
int[] array = { 10, 20, 30, 40, 50 };
Span<int> span = array.AsSpan();
Span<int> slice = span.Slice(1, 3);
slice[0] = 99;
Console.WriteLine(string.Join(", ", array));
Span<int> stackSpan = stackalloc int[3];
stackSpan[0] = 1;
stackSpan[1] = 2;
stackSpan[2] = 3;
Console.WriteLine(string.Join(", ", stackSpan.ToArray()));
}
}
TEXT
10, 99, 30, 40, 50
1, 2, 3
⚠️
Span<T> 只能存储在栈上,不能作为类字段或跨 await 使用。需要跨栈帧时使用 Memory<T>。
正则表达式(Regex)
System.Text.RegularExpressions.Regex 类用于模式匹配、搜索和替换文本。
常用操作
CSHARP
using System;
using System.Text.RegularExpressions;
class Program
{
static void Main()
{
string input = "联系电话: 138-1234-5678, 邮箱: test@example.com";
bool hasPhone = Regex.IsMatch(input, @"\d{3}-\d{4}-\d{4}");
Console.WriteLine($"包含电话: {hasPhone}");
Match phoneMatch = Regex.Match(input, @"\d{3}-\d{4}-\d{4}");
Console.WriteLine($"电话: {phoneMatch.Value}");
MatchCollection emails = Regex.Matches(input, @"[\w.]+@[\w.]+");
foreach (Match m in emails)
Console.WriteLine($"邮箱: {m.Value}");
string replaced = Regex.Replace(input, @"\d{4}", "****");
Console.WriteLine($"替换: {replaced}");
}
}
TEXT
包含电话: True
电话: 138-1234-5678
邮箱: test@example.com
替换: 联系电话: 138-**-**, 邮箱: test@example.com
常用模式速查
| 模式 | 说明 |
|---|---|
\d |
数字 |
\w |
字母/数字/下划线 |
\s |
空白字符 |
. |
任意字符(除换行) |
^ / $ |
行首 / 行尾 |
{n,m} |
重复 n 到 m 次 |
[abc] |
字符集 |
不安全代码(unsafe)
unsafe 上下文允许使用指针和手动内存操作,适用于互操作或极致性能场景。
指针与 fixed
CSHARP
using System;
class Program
{
static unsafe void Main()
{
int value = 42;
int* ptr = &value;
Console.WriteLine($"值: {*ptr}");
Console.WriteLine($"地址: {(long)ptr}");
int[] arr = { 1, 2, 3 };
fixed (int* p = arr)
{
Console.WriteLine($"首元素: {*p}");
Console.WriteLine($"第二元素: {*(p + 1)}");
}
}
}
TEXT
值: 42
地址: 12345678
首元素: 1
第二元素: 2
⚠️ 使用
unsafe 需要在项目文件中启用 <AllowUnsafeBlocks>true</AllowUnsafeBlocks>。fixed 语句固定对象防止 GC 移动内存。
C# 版本特性一览(7~12)
| 版本 | 关键特性 | 简要说明 |
|---|---|---|
| C# 7 | out 变量、元组、模式匹配 | out int x 内联声明,(int, string) 元组,is Type 模式 |
| C# 7 | 本地函数、ref 返回 | 方法内嵌套函数,引用返回值 |
| C# 8 | 可空引用类型、异步流 | string? 可空标注,IAsyncEnumerable<T> |
| C# 8 | 默认接口方法、索引与范围 | 接口默认实现,[^1]、[1..3] |
| C# 9 | 记录类型、init 设置器 | record 不可变类型,init 只初始化属性 |
| C# 9 | 顶级语句、目标类型 new | 无需 Main 模板,new() 省略类型 |
| C# 10 | global using、文件范围命名空间 | global using X;,namespace Ns; 单行 |
| C# 10 | 记录结构体、const 插值字符串 | record struct,常量内插字符串 |
| C# 11 | 原始字符串字面量、required | """...""" 多行原始串,required 强制初始化 |
| C# 11 | 列表模式、UTF8 字面量 | [1, 2, ..] 匹配,"hello"u8 |
| C# 12 | 主构造函数 | class C(int x) 类声明即构造 |
| C# 12 | 集合表达式、别名任意类型 | [1, 2, 3] 统一初始化,using P = int* |
💡 本课为高级特性概览,每个主题后续均有独立课程深入讲解。
❓ 常见问题
Q 反射有性能开销吗?
A 有,反射比直接调用慢数十倍,频繁使用应缓存 MethodInfo 或改用表达式树。
Q:Span<T> 和 Memory<T> 有何区别? A:Span 只能存于栈,不可跨 async/存储;Memory 可存于堆,可跨异步边界,通过 .Span 访问。
Q:yield return 是多线程的吗? A:不是,yield 是单线程的惰性枚举机制,元素在 MoveNext() 调用时才计算。
Q:什么时候需要 unsafe 代码? A:需要与 C/C++ 互操作、操作非托管内存或追求极致性能时使用,日常开发应避免。
📖 小节
- 反射通过 Type 类在运行时获取类型元数据并动态创建实例
- Attribute 为代码添加声明性元数据,支持自定义与运行时读取
- 泛型约束(where)限制类型参数为引用类型、值类型、有构造函数等
- yield return 实现惰性迭代,yield break 终止枚举
- Span<T> 提供栈上零拷贝切片,Memory<T> 可跨异步边界
- Regex 类支持模式匹配、搜索、替换等文本操作
- unsafe 上下文允许指针操作,需 fixed 固定内存
- C# 7~12 持续引入元组、可空引用、记录类型、主构造函数等特性
📝 作业
- 使用反射获取
string类的所有公共方法名称并输出 - 创建一个自定义
[Author]特性,标注在类上并通过反射读取 - 编写一个泛型方法
T Create<T>() where T : new(),创建并返回实例 - 用 yield return 实现一个生成斐波那契数列前 20 项的迭代器
- 使用 Regex 验证一个字符串是否为合法的 IPv4 地址格式



