C# 高级特性

1 minAdvanced2026/6/14

反射、特性(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+ 推荐模式(编译器默认生成的也是线程安全的)
// 直接使用 ?. 操作符即可