1. C#预处理指令深度解析
预处理指令是C#中一个强大但常被忽视的功能,它允许开发者在编译阶段控制代码的编译行为。与C/C++不同,C#的预处理指令功能更为有限,但在条件编译、代码组织等方面仍然非常实用。
1.1 基本预处理指令类型
C#支持以下几种主要的预处理指令:
#define和#undef:用于定义和取消定义条件编译符号#if、#elif、#else、#endif:用于条件编译#error和#warning:生成编译时错误和警告#line:控制编译器输出的行号和文件名#region和#endregion:组织代码块#pragma:提供编译器特定指令
1.2 条件编译实战
条件编译是预处理指令最常见的用途。假设我们有一个需要支持多平台的应用:
#define ANDROID //#define IOS using System; class PlatformService { public void ShowMessage() { #if ANDROID Console.WriteLine("Running on Android platform"); #elif IOS Console.WriteLine("Running on iOS platform"); #else Console.WriteLine("Running on unknown platform"); #endif } }在实际项目中,这些定义通常不是在代码中硬编码,而是在项目属性或构建脚本中设置。
重要提示:条件编译符号是区分大小写的,
DEBUG和debug会被视为不同的符号。
1.3 诊断指令的应用
#error和#warning可以在特定条件下生成编译时消息:
#if NET40 #error .NET 4.0 is no longer supported #endif #warning This method will be deprecated in next version public void LegacyMethod() { // ... }这在以下场景特别有用:
- 标记即将废弃的API
- 提醒未完成的功能
- 强制使用特定编译配置
2. C#反射机制全面剖析
反射是C#中强大的元编程能力,允许程序在运行时检查、修改甚至生成代码。虽然反射会带来一定的性能开销,但在很多场景下是不可替代的。
2.1 Type获取的三种方式
获取Type对象是反射操作的起点,主要有三种方式:
// 1. 使用typeof运算符 Type type1 = typeof(string); // 2. 使用GetType()实例方法 string s = "hello"; Type type2 = s.GetType(); // 3. 使用Type.GetType静态方法 Type type3 = Type.GetType("System.String");每种方式适用不同场景:
typeof在编译时就知道类型时使用GetType()在只有对象实例时使用Type.GetType()在只有类型名称字符串时使用
2.2 反射核心操作示例
通过反射可以动态调用方法、访问属性等:
using System; using System.Reflection; class Program { static void Main() { Type mathType = typeof(Math); // 获取方法信息 MethodInfo sqrtMethod = mathType.GetMethod("Sqrt", new[] { typeof(double) }); // 调用静态方法 double result = (double)sqrtMethod.Invoke(null, new object[] { 16.0 }); Console.WriteLine($"Square root of 16 is {result}"); // 创建实例并设置属性 Type personType = typeof(Person); object person = Activator.CreateInstance(personType); PropertyInfo nameProp = personType.GetProperty("Name"); nameProp.SetValue(person, "John Doe"); Console.WriteLine($"Person name: {nameProp.GetValue(person)}"); } } class Person { public string Name { get; set; } }2.3 反射性能优化技巧
反射虽然强大但性能较差,以下是一些优化建议:
- 缓存反射结果:将MethodInfo、PropertyInfo等存储在静态变量中
- 使用Delegate.CreateDelegate:将方法转换为委托
- 使用dynamic关键字:在已知接口的情况下
- 使用表达式树:构建并编译动态代码
优化后的反射调用示例:
// 普通反射调用 MethodInfo method = typeof(MyClass).GetMethod("MyMethod"); method.Invoke(instance, new object[] { param }); // 优化后的调用 - 创建并缓存委托 private static Action<MyClass, int> cachedDelegate; public static void FastReflectionCall(MyClass instance, int param) { if (cachedDelegate == null) { MethodInfo method = typeof(MyClass).GetMethod("MyMethod"); cachedDelegate = (Action<MyClass, int>)Delegate.CreateDelegate( typeof(Action<MyClass, int>), method); } cachedDelegate(instance, param); }3. 关键类深度解析
3.1 Type类核心功能
Type类是反射系统的核心,提供以下关键功能:
类型信息查询:
IsClass、IsInterface、IsValueType等判断类型种类GetInterfaces()获取实现的接口BaseType获取基类
成员访问:
GetMethods()、GetProperties()、GetFields()等GetMember()获取特定成员GetCustomAttributes()获取自定义特性
实例操作:
Activator.CreateInstance()创建实例InvokeMember()动态调用成员
3.2 Assembly类的关键作用
Assembly类代表程序集,是反射的另一个核心类:
// 加载程序集 Assembly assembly = Assembly.LoadFrom("MyLibrary.dll"); // 获取所有公共类型 Type[] types = assembly.GetExportedTypes(); // 获取特定类型 Type targetType = assembly.GetType("MyNamespace.MyClass"); // 获取程序集信息 AssemblyName name = assembly.GetName(); Console.WriteLine($"Assembly: {name.Name}, Version: {name.Version}");3.3 MethodInfo和PropertyInfo详解
这两个类分别封装了方法和属性的元数据:
MethodInfo关键功能:
ReturnType:获取返回类型GetParameters():获取参数信息Invoke():调用方法IsStatic:判断是否为静态方法
PropertyInfo关键功能:
PropertyType:获取属性类型GetValue()/SetValue():获取/设置属性值CanRead/CanWrite:判断可读可写性
4. 高级应用场景
4.1 动态插件系统实现
反射常用于实现插件架构:
public interface IPlugin { string Name { get; } void Execute(); } public class PluginLoader { public List<IPlugin> LoadPlugins(string path) { var plugins = new List<IPlugin>(); foreach (string file in Directory.GetFiles(path, "*.dll")) { try { Assembly assembly = Assembly.LoadFrom(file); foreach (Type type in assembly.GetTypes()) { if (typeof(IPlugin).IsAssignableFrom(type) && !type.IsAbstract) { IPlugin plugin = (IPlugin)Activator.CreateInstance(type); plugins.Add(plugin); } } } catch (Exception ex) { Console.WriteLine($"Failed to load {file}: {ex.Message}"); } } return plugins; } }4.2 动态代理实现
利用反射可以实现AOP风格的动态代理:
public class DynamicProxy<T> : DispatchProxy { private T _decorated; protected override object Invoke(MethodInfo targetMethod, object[] args) { try { Console.WriteLine($"Before {targetMethod.Name}"); var result = targetMethod.Invoke(_decorated, args); Console.WriteLine($"After {targetMethod.Name}"); return result; } catch (Exception ex) when (ex is TargetInvocationException) { Console.WriteLine($"Error in {targetMethod.Name}: {ex.InnerException?.Message}"); throw ex.InnerException ?? ex; } } public static T Create(T decorated) { object proxy = Create<T, DynamicProxy<T>>(); ((DynamicProxy<T>)proxy)._decorated = decorated; return (T)proxy; } }4.3 序列化/反序列化工具
反射可以用于实现通用的序列化工具:
public class ObjectSerializer { public string Serialize(object obj) { var builder = new StringBuilder(); Type type = obj.GetType(); builder.AppendLine($"Type: {type.FullName}"); foreach (PropertyInfo prop in type.GetProperties()) { if (prop.CanRead) { object value = prop.GetValue(obj); builder.AppendLine($"{prop.Name}: {value}"); } } return builder.ToString(); } public T Deserialize<T>(string data) where T : new() { var lines = data.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); T result = new T(); Type type = typeof(T); foreach (string line in lines.Skip(1)) // Skip type line { var parts = line.Split(new[] { ':' }, 2); if (parts.Length == 2) { string propName = parts[0].Trim(); string valueStr = parts[1].Trim(); PropertyInfo prop = type.GetProperty(propName); if (prop != null && prop.CanWrite) { object value = Convert.ChangeType(valueStr, prop.PropertyType); prop.SetValue(result, value); } } } return result; } }5. 性能考量与最佳实践
5.1 反射性能对比
| 操作 | 直接调用 | 反射调用 | 优化后反射 |
|---|---|---|---|
| 方法调用 | 1x | ~100x | ~2x |
| 属性访问 | 1x | ~50x | ~1.5x |
| 类型检查 | 1x | ~10x | N/A |
5.2 反射最佳实践
- 避免频繁反射:在循环中避免使用反射
- 适当缓存:缓存MethodInfo、PropertyInfo等对象
- 使用接口约束:尽可能使用接口或基类约束
- 考虑替代方案:
- 对于已知类型,使用dynamic
- 对于高性能场景,使用表达式树或IL生成
- 安全考虑:反射会绕过访问修饰符限制,需谨慎使用
5.3 表达式树优化示例
对于需要高性能的动态调用,可以使用表达式树:
public static class PropertyAccessor { private static readonly Dictionary<string, Delegate> cache = new Dictionary<string, Delegate>(); public static Func<T, object> CreateGetAccessor<T>(string propertyName) { string key = $"{typeof(T).FullName}.{propertyName}"; if (!cache.TryGetValue(key, out var accessor)) { ParameterExpression param = Expression.Parameter(typeof(T), "instance"); MemberExpression property = Expression.Property(param, propertyName); UnaryExpression convert = Expression.Convert(property, typeof(object)); accessor = Expression.Lambda<Func<T, object>>(convert, param).Compile(); cache[key] = accessor; } return (Func<T, object>)accessor; } public static Action<T, object> CreateSetAccessor<T>(string propertyName) { string key = $"{typeof(T).FullName}.{propertyName}.set"; if (!cache.TryGetValue(key, out var accessor)) { ParameterExpression instanceParam = Expression.Parameter(typeof(T), "instance"); ParameterExpression valueParam = Expression.Parameter(typeof(object), "value"); MemberExpression property = Expression.Property(instanceParam, propertyName); UnaryExpression convertedValue = Expression.Convert(valueParam, property.Type); BinaryExpression assign = Expression.Assign(property, convertedValue); accessor = Expression.Lambda<Action<T, object>>(assign, instanceParam, valueParam).Compile(); cache[key] = accessor; } return (Action<T, object>)accessor; } }这种方式的性能接近直接调用,同时保留了动态性。