1. 理解C#中的初始化概念在C#开发中初始化是确保对象或变量处于可用状态的关键步骤。有些类型会自动获得默认值而有些则需要我们显式处理。这个问题看似基础但实际开发中经常因为理解不透彻导致空引用异常或逻辑错误。我刚入行时曾在一个电商项目里踩过坑 - 凌晨三点追查一个诡异的NullReferenceException最后发现是忘记初始化某个自定义集合类型。这种经历让我深刻认识到理解哪些类型需要手动初始化是写出健壮C#代码的基本功。2. 自动初始化的类型2.1 值类型的默认行为C#中的值类型struct、enum、数值类型等在声明时会自动初始化int number; // 自动初始化为0 bool flag; // 自动初始化为false DateTime date; // 自动初始化为DateTime.MinValue但要注意这些默认值可能不符合业务逻辑。比如int productCount; // 实际业务中0可能表示无商品但这里可能是忘记赋值2.2 引用类型的默认值类(class)等引用类型默认初始化为nullstring name; // null Listint nums; // null MyClass obj; // null这其实是一种伪初始化 - 虽然语法上合法但直接使用会抛出NullReferenceException。3. 必须手动初始化的类型3.1 集合类型所有System.Collections和System.Collections.Generic下的集合类型都需要显式初始化Liststring names new Liststring(); Dictionaryint, string dict new Dictionaryint, string();警告忘记初始化集合是新手最常见的错误之一。我曾见过团队花两天排查的bug最终发现是忘了初始化某个配置字典。3.2 自定义类实例任何自定义class都需要new初始化public class User { /*...*/ } User user new User(); // 必须显式初始化3.3 可为null的值类型虽然值类型有默认值但声明为可空(nullable)时需要特别注意int? nullableInt null; // 明确赋值为null DateTime? date null; // 比默认的MinValue更能表达无值语义3.4 委托和事件委托必须初始化后才能调用Actionstring logger null; // 必须至少赋值为null logger?.Invoke(test); // 安全调用 // 更好的做法 Actionstring logger Console.WriteLine; // 直接初始化4. 特殊场景下的初始化4.1 数组的两种初始化方式数组是引用类型需要特别注意int[] nums1 new int[3]; // 初始化长度为3元素为0 int[] nums2 {1, 2, 3}; // 初始化并赋值 int[] nums3 new int[] {1,2,3}; // 完整语法4.2 结构体的字段初始化struct的字段不会自动初始化public struct Point { public int X; // 不会自动初始化为0 public int Y; } var p new Point(); Console.WriteLine(p.X); // 编译器警告可能未初始化正确做法是提供构造函数public struct Point { public int X; public int Y; public Point(int x, int y) { X x; Y y; } }4.3 延迟初始化模式有些场景适合延迟初始化private LazyListstring _names new LazyListstring(() { return LoadNamesFromDB(); // 首次访问时才会执行 });5. 初始化的最佳实践5.1 声明时立即初始化推荐做法Liststring names new Liststring(); // 立即初始化 Dictionaryint, string mapping new Dictionaryint, string();5.2 使用对象初始化器C#提供了简洁的初始化语法var user new User { Name 张三, Age 30 }; var dict new Dictionaryint, string { [1] 一, [2] 二 };5.3 在构造函数中初始化确保字段始终有效public class OrderService { private readonly ListOrder _orders; public OrderService() { _orders new ListOrder(); // 构造函数中初始化 } }5.4 使用null条件运算符处理可能未初始化的引用Liststring names possiblyNullList ?? new Liststring();6. 静态成员的初始化6.1 静态构造函数适合复杂的静态初始化public class Config { public static readonly Dictionarystring, string Settings; static Config() { Settings LoadConfigFromFile(); // 静态构造函数中初始化 } }6.2 静态字段初始化器简单静态字段可以直接初始化public static class MathConstants { public static readonly double Pi 3.1415926; }7. 需要特别注意的陷阱7.1 部分初始化的对象当构造函数抛出异常时public class ResourceHolder { private FileStream _file; public ResourceHolder(string path) { _file File.Open(path); // 可能抛出异常 // 其他初始化... } }解决方案public ResourceHolder(string path) { var file File.Open(path); // 先创建临时变量 // 其他初始化... _file file; // 最后赋值字段 }7.2 循环引用初始化相互依赖的类型要注意初始化顺序public class A { public B BInstance { get; set; } } public class B { public A AInstance { get; set; } } // 正确初始化方式 var a new A(); var b new B(); a.BInstance b; b.AInstance a;7.3 多线程环境下的初始化使用双重检查锁定模式private static readonly object _lock new object(); private static Liststring _sharedList; public static Liststring SharedList { get { if (_sharedList null) { lock (_lock) { if (_sharedList null) { _sharedList new Liststring(); } } } return _sharedList; } }8. 工具辅助检查8.1 编译器警告开启所有警告并视为错误PropertyGroup TreatWarningsAsErrorstrue/TreatWarningsAsErrors WarningLevel4/WarningLevel /PropertyGroup8.2 静态代码分析使用Roslyn分析器检测未初始化字段PackageReference IncludeMicrosoft.CodeAnalysis.NetAnalyzers Version5.0.3 /8.3 单元测试验证编写初始化测试[TestMethod] public void Constructor_ShouldInitializeAllCollections() { var service new OrderService(); Assert.IsNotNull(service.Orders); Assert.IsNotNull(service.Customers); }9. 性能考量9.1 延迟初始化的权衡延迟初始化可以提升启动性能但会增加运行时开销private ListLogEntry _logs; public ListLogEntry Logs { get { if (_logs null) _logs LoadLogs(); // 按需加载 return _logs; } }9.2 大对象的初始化对于占用内存大的对象// 不好的做法 BigObject big new BigObject(); // 立即分配大内存 // 更好的做法 LazyBigObject big new LazyBigObject(() new BigObject());10. C# 9/10中的改进10.1 记录(record)类型的初始化record类型简化了初始化public record Person(string Name, int Age); var p new Person(李四, 25); // 不可变对象的简洁初始化10.2 顶级语句中的初始化在顶级语句中也要保持良好习惯Liststring names new(); // C# 9的目标类型new表达式 // 而不是 Liststring names; // 编译通过但危险10.3 必需的初始化器C# 11引入required修饰符public class User { public required string Name { get; set; } } var user new User { Name 王五 }; // 必须初始化Name
