C# 高级特性
00:00
反射、特性(Attribute)、动态编程(dynamic)、Span/Memory、ref struct、不安全代码、指针、委托与事件、多播委托
1. 反射
1.1 获取类型信息
// 获取 Type 对象
Type type1 = typeof(string);
Type type2 = "hello".GetType();
Type? type3 = Type.GetType("System.String");
// 类型信息
Console.WriteLine(type1.Name); // String
Console.WriteLine(type1.FullName); // System.String
Console.WriteLine(type1.Namespace); // System
Console.WriteLine(type1.IsClass); // True
Console.WriteLine(type1.IsValueType); // False
Console.WriteLine(type1.IsEnum); // False
Console.WriteLine(type1.IsAbstract); // False
Console.WriteLine(type1.IsGenericType); // False
1.2 检查成员
// 获取所有公共属性
var properties = typeof(Product).GetProperties();
foreach (var prop in properties)
{
Console.WriteLine($"{prop.Name}: {prop.PropertyType.Name}");
}
// 获取方法
var methods = typeof(List<int>).GetMethods(BindingFlags.Public | BindingFlags.Instance);
// 获取字段
var fields = typeof(Product).GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
// 获取构造函数
var ctors = typeof(Product).GetConstructors();
// 获取特性
var attrs = typeof(Product).GetCustomAttributes<SerializableAttribute>();
// 泛型类型信息
var listType = typeof(List<>);
var intListType = listType.MakeGenericType(typeof(int));
Console.WriteLine(intListType.FullName); // System.Collections.Generic.List`1[[System.Int32, ...]]
1.3 动态创建与调用
// 动态创建实例
var type = typeof(Product);
var product = Activator.CreateInstance(type) as Product;
// 使用构造函数创建
var ctor = type.GetConstructor([typeof(string), typeof(decimal)]);
var instance = ctor?.Invoke(["商品A", 99.9m]);
// 动态调用方法
var method = typeof(Console).GetMethod("WriteLine", [typeof(string)]);
method?.Invoke(null, ["Hello, Reflection!"]);
// 动态获取/设置属性
var prop = typeof(Product).GetProperty("Name");
prop?.SetValue(instance, "新名称");
var value = prop?.GetValue(instance);
// 动态调用泛型方法
var genericMethod = typeof(Enumerable).GetMethod("Where");
var specialized = genericMethod?.MakeGenericMethod(typeof(int));
1.4 反射性能优化
// 直接反射 - 慢
var method = typeof(Math).GetMethod("Max");
var result = method.Invoke(null, [1, 2]);
// 缓存 MethodInfo
private static readonly MethodInfo MaxMethod =
typeof(Math).GetMethod("Max", [typeof(int), typeof(int)])!;
// 委托缓存(更快)
private static readonly Func<int, int, int> MaxDelegate =
(Func<int, int, int>)Delegate.CreateDelegate(
typeof(Func<int, int, int>), MaxMethod);
// 表达式树编译(最快之一)
var param1 = Expression.Parameter(typeof(int), "a");
var param2 = Expression.Parameter(typeof(int), "b");
var call = Expression.Call(typeof(Math), "Max", null, param1, param2);
var lambda = Expression.Lambda<Func<int, int, int>>(call, param1, param2);
var compiled = lambda.Compile();
// Source Generator(编译期,零运行时开销)
// 现代 .NET 推荐使用 Source Generator 替代运行时反射
2. 特性 (Attribute)
2.1 内置特性
// Obsolete - 标记过时
[Obsolete("请使用 NewMethod 代替")]
public void OldMethod() { }
[Obsolete("必须迁移", error: true)] // 编译错误
public void VeryOldMethod() { }
// Serializable / NonSerialized
[Serializable]
public class Config
{
public string Name { get; set; }
[NonSerialized]
private string _tempData; // 不参与序列化
}
// Conditional - 条件编译
[Conditional("DEBUG")]
public void DebugLog(string message) => Console.WriteLine(message);
// DebuggerDisplay - 调试器显示
[DebuggerDisplay("Product: {Name} (${Price})")]
public class Product(string Name, decimal Price);
// CallerMemberName - 调用方信息
public void Log(
string message,
[CallerMemberName] string member = "",
[CallerFilePath] string file = "",
[CallerLineNumber] int line = 0)
{
Console.WriteLine($"[{file}:{line}] {member}: {message}");
}
2.2 自定义特性
// 定义特性
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class ApiRouteAttribute : Attribute
{
public string Route { get; }
public string Method { get; set; } = "GET";
public ApiRouteAttribute(string route)
{
Route = route;
}
}
// 使用特性
[ApiRoute("/api/users", Method = "GET")]
[ApiRoute("/api/users/{id}", Method = "GET")]
public class UserController
{
[ApiRoute("/api/users", Method = "POST")]
public void CreateUser() { }
}
// 运行时读取特性
var attrs = typeof(UserController).GetCustomAttributes<ApiRouteAttribute>();
foreach (var attr in attrs)
{
Console.WriteLine($"{attr.Method} {attr.Route}");
}
3. 动态编程 (dynamic)
// dynamic 类型 - 运行时解析
dynamic obj = "Hello";
Console.WriteLine(obj.Length); // 5,运行时调用 string.Length
obj = 42;
Console.WriteLine(obj + 8); // 50,运行时调用 int 运算
// 与反射对比
dynamic product = Activator.CreateInstance(typeof(Product))!;
product.Name = "动态商品"; // 比 reflection.SetValue 更直观
// COM 互操作
// dynamic excel = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application")!);
// excel.Visible = true;
// excel.Workbooks.Add();
// ExpandoObject - 动态对象
dynamic person = new ExpandoObject();
person.Name = "张三";
person.Age = 25;
person.Greet = (Func<string>)(() => $"你好,我是{person.Name}");
Console.WriteLine(person.Greet()); // 你好,我是张三
// 注意:dynamic 丧失编译期类型检查,性能较差
// 现代 C# 更推荐使用模式匹配和泛型替代 dynamic
4. Span<T> 与 Memory<T>
4.1 Span<T> - 零分配切片
// 从数组创建
int[] array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
Span<int> span = array;
// 切片(零分配!)
Span<int> slice = span[2..5]; // [3, 4, 5]
// 修改
slice[0] = 99; // array[2] 也变为 99
// 从字符串创建
ReadOnlySpan<char> text = "Hello, World!".AsSpan();
ReadOnlySpan<char> hello = text[..5]; // "Hello"
// 从 stackalloc 创建
Span<int> buffer = stackalloc int[256]; // 栈上分配
// 常用操作
span.Fill(0); // 填充
span.Clear(); // 清零
span.Reverse(); // 反转
int index = span.IndexOf(5); // 查找
bool contains = span.Contains(3); // 包含
span.CopyTo(destination); // 复制
4.2 Memory<T> - 可存储的切片
// Span 不能存储在字段或 await 边界之后
// Memory<T> 可以
public class DataProcessor
{
// 不允许
// private Span<int> _data;
// 使用 Memory<T>
private Memory<int> _data;
public DataProcessor(Memory<int> data) => _data = data;
public async Task ProcessAsync()
{
// 在 await 之前获取 Span
ProcessSpan(_data.Span);
await Task.Delay(100);
ProcessSpan(_data.Span); // await 之后重新获取
}
private void ProcessSpan(Span<int> data)
{
for (int i = 0; i < data.Length; i++)
{
data[i] *= 2;
}
}
}
4.3 ref struct
// ref struct 只能存在于栈上
public ref struct SpanTokenizer
{
private ReadOnlySpan<char> _source;
private int _position;
public SpanTokenizer(ReadOnlySpan<char> source)
{
_source = source;
_position = 0;
}
public bool MoveNext()
{
while (_position < _source.Length && char.IsWhiteSpace(_source[_position]))
_position++;
return _position < _source.Length;
}
public ReadOnlySpan<char> Current
{
get
{
int start = _position;
while (_position < _source.Length && !char.IsWhiteSpace(_source[_position]))
_position++;
return _source[start.._position];
}
}
}
// ref struct 限制:
// - 不能装箱
// - 不能作为类字段
// - 不能被 Lambda 捕获
// - 不能实现接口(C# 13 之前)
// - C# 13 允许 ref struct 实现接口
5. 不安全代码与指针
// unsafe 块
unsafe
{
int value = 42;
int* ptr = &value;
Console.WriteLine(*ptr); // 42
*ptr = 100;
Console.WriteLine(value); // 100
}
// 指针与数组
unsafe
{
int[] array = new int[10];
fixed (int* ptr = array) // 固定数组,防止 GC 移动
{
for (int i = 0; i < 10; i++)
{
ptr[i] = i * i;
}
}
}
// stackalloc - 栈上分配
Span<byte> buffer = stackalloc byte[1024]; // 无 GC 压力
// NativeMemory 互操作
unsafe
{
byte* native = (byte*)NativeMemory.Alloc(1024);
try
{
new Span<byte>(native, 1024).Fill(0);
// 使用 native 内存...
}
finally
{
NativeMemory.Free(native);
}
}
// 函数指针(C# 9+)
delegate*<int, int, int> addPtr = &Add;
int result = addPtr(3, 4); // 7
static int Add(int a, int b) => a + b;
// unmanaged 约束
public static T Zero<T>() where T : unmanaged
{
// T 保证是非托管类型,可以使用指针操作
return default;
}
6. 委托与事件
6.1 委托
// 自定义委托
public delegate bool Predicate<T>(T item);
public delegate void NotifyHandler(string message);
// 内置委托
Action action = () => Console.WriteLine("无参数");
Action<string> log = msg => Console.WriteLine(msg);
Action<string, int> logWithLevel = (msg, level) => { };
Func<int> getRandom = () => Random.Shared.Next();
Func<int, int, int> add = (a, b) => a + b;
Func<string, bool> isEmpty = s => string.IsNullOrEmpty(s);
Predicate<int> isPositive = n => n > 0;
// 委托组合
Func<int, int> doubleIt = x => x * 2;
Func<int, int> addOne = x => x + 1;
Func<int, int> composed = x => doubleIt(addOne(x)); // 先+1再×2
Console.WriteLine(composed(3)); // 8
6.2 多播委托
// 多播委托 - 一个委托调用多个方法
Action<string> logger = msg => Console.WriteLine($"控制台: {msg}");
logger += msg => Debug.WriteLine($"调试: {msg}");
logger += msg => File.AppendAllText("log.txt", $"文件: {msg}\n");
logger("测试消息"); // 三个方法都会被调用
// 移除
logger -= msg => Console.WriteLine($"控制台: {msg}"); // 注意:Lambda 无法精确移除
// 正确做法:使用方法组
logger += ConsoleLog;
logger += FileLog;
logger -= ConsoleLog; // 可以精确移除
void ConsoleLog(string msg) => Console.WriteLine(msg);
void FileLog(string msg) => File.AppendAllText("log.txt", msg + "\n");
// 获取调用列表
var delegates = logger.GetInvocationList();
foreach (var d in delegates)
{
d.DynamicInvoke("逐个调用");
}
6.3 事件
// 标准事件模式
public class Stock
{
public string Symbol { get; }
private decimal _price;
// 事件声明
public event EventHandler<PriceChangedEventArgs>? PriceChanged;
public Stock(string symbol, decimal price)
{
Symbol = symbol;
_price = price;
}
public decimal Price
{
get => _price;
set
{
if (_price != value)
{
var oldPrice = _price;
_price = value;
// 触发事件
OnPriceChanged(new PriceChangedEventArgs(Symbol, oldPrice, value));
}
}
}
protected virtual void OnPriceChanged(PriceChangedEventArgs e)
{
PriceChanged?.Invoke(this, e);
}
}
public class PriceChangedEventArgs : EventArgs
{
public string Symbol { get; }
public decimal OldPrice { get; }
public decimal NewPrice { get; }
public decimal ChangePercent => (NewPrice - OldPrice) / OldPrice * 100;
public PriceChangedEventArgs(string symbol, decimal oldPrice, decimal newPrice)
{
Symbol = symbol;
OldPrice = oldPrice;
NewPrice = newPrice;
}
}
// 订阅事件
var stock = new Stock("AAPL", 150m);
stock.PriceChanged += (sender, e) =>
{
Console.WriteLine($"{e.Symbol}: {e.OldPrice} → {e.NewPrice} ({e.ChangePercent:+0.00;-0.00}%)");
};
stock.Price = 155m;
6.4 线程安全事件
// 线程安全的事件触发模式
public class SafeEventPublisher
{
// 使用 Volatile.Read 确保读取最新值
public event EventHandler<string>? MessageReceived;
public void OnMessage(string message)
{
// 捕获当前委托引用,避免竞态条件
var handler = Volatile.Read(ref MessageReceived);
handler?.Invoke(this, message);
}
}
// .NET 4.5+ 推荐模式(编译器默认生成的也是线程安全的)
// 直接使用 ?. 操作符即可