想象你正在设计一个动物园管理系统。狮子、企鹅、蛇这些动物虽然特性各异,但都可以用"动物"这个抽象概念来描述。在C#中,类就是这样的抽象容器。具体来看:
private int _age表示年龄这个内在属性public void Hunt()描述捕猎这个动作public string FurColor {get; set;}让外部可以安全访问毛发颜色csharp复制// 生物类比示例
public class Animal {
// 内在状态
private int _energy = 100;
// 对外接口
public string Species { get; set; }
// 行为能力
public void Move() {
_energy -= 10;
Console.WriteLine($"{Species} is moving...");
}
}
关键认知:类就像生物学的"属"概念,定义了某类实体的基本特征和行为模板。当我们需要具体个体时,就用
new Animal()实例化出"狮子辛巴"这样的对象。
封装不是简单的"把东西包起来",而是有层次的代码组织哲学:
基础封装:用private保护字段
csharp复制private string _password; // 外部无法直接访问
智能封装:通过属性控制访问
csharp复制private int _age;
public int Age {
get => _age;
set => _age = value > 0 ? value : throw new Exception("年龄必须为正数");
}
业务封装:用方法组合操作
csharp复制public void TransferMoney(Account target, decimal amount) {
if(_balance >= amount) {
_balance -= amount;
target.Deposit(amount);
}
}
我在金融系统开发中曾遇到一个经典案例:最初设计的Transaction类包含了交易金额、货币类型、时间戳等30多个属性。这种"上帝类"导致:
后来通过单一职责原则重构为:
csharp复制public class TransactionHeader { /* 元信息 */ }
public class TransactionBody { /* 核心数据 */ }
public class TransactionExtension { /* 扩展数据 */ }
血泪教训:类的字段数量控制在7±2个(心理学中的米勒定律),超过就该考虑拆分。
当你在Visual Studio点击生成时,背后发生了这些关键步骤:
Student.cs等源代码转为抽象语法树bash复制# 手动编译观察过程
csc /out:MyApp.exe Program.cs Student.cs
# 用ILDASM查看生成的程序集结构
ildasm MyApp.exe
在电商系统升级时,我们采用这样的版本管理方案:
xml复制<!-- AssemblyInfo.cs 示例 -->
[assembly: AssemblyVersion("2.1.0.0")]
[assembly: AssemblyFileVersion("2.1.3.0")]
[assembly: AssemblyInformationalVersion("2.1.3-beta")]
重要发现:NuGet包版本应与程序集版本解耦,前者遵循SemVer规范,后者保持稳定更利于依赖管理。
大型ERP系统的典型程序集划分:
code复制ERP.sln
├── ERP.Core (基础类型)
├── ERP.Inventory (库存模块)
├── ERP.Accounting (财务模块)
├── ERP.Reporting (报表引擎)
└── ERP.Web (表现层)
每个程序集应满足:
理解以下访问修饰符的组合效果:
| 修饰符 | 类内部 | 同一程序集 | 不同程序集子类 | 完全外部 |
|---|---|---|---|---|
| private | ✓ | |||
| internal | ✓ | ✓ | ||
| protected | ✓ | ✓ | ✓ | |
| protected internal | ✓ | ✓ | ✓ | |
| public | ✓ | ✓ | ✓ | ✓ |
插件系统常用Assembly.LoadFrom实现热插拔:
csharp复制// 加载插件程序集
var pluginAssembly = Assembly.LoadFrom("Payment/AlipayPlugin.dll");
// 获取插件入口类型
var pluginType = pluginAssembly.GetType("Alipay.PaymentProcessor");
// 创建实例并转换接口
var processor = (IPaymentProcessor)Activator.CreateInstance(pluginType);
// 使用插件功能
processor.ProcessPayment(order);
性能提示:频繁加载/卸载程序集应使用
AssemblyLoadContext实现隔离,避免内存泄漏。
保护商业程序集的常见方案:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 代码混淆 | 成本低,兼容性好 | 可逆性强 | 一般商业软件 |
| 原生映像(NGen) | 提升启动速度 | 需要管理员权限安装 | 客户端应用程序 |
| 加密壳 | 防护强度高 | 可能被杀毒软件误报 | 高价值算法保护 |
| 源码保护编译器 | 无中间代码 | 绑定特定硬件 | 金融安全领域 |
当遇到TypeLoadException时,按以下步骤诊断:
ildasm或dotnet list package)powershell复制# 查看程序集依赖
dotnet publish --self-contained false -r win-x64
在某物流平台项目中,通过以下优化将启动时间从12秒降至3秒:
ILMerge将20+工具类库合并为Utility.dllAssembly.LoadOnDemand.NET Core时代的多目标构建配置:
xml复制<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;net6.0-android;net6.0-ios</TargetFrameworks>
</PropertyGroup>
<!-- 平台特定代码条件编译 -->
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0-android'">
<Compile Include="Platforms/Android/**/*.cs" />
</ItemGroup>
</Project>
C# 9引入的源生成器技术,可以在编译时动态生成类:
csharp复制// 自动为标记[GenerateDTO]的类创建DTO版本
[Generator]
public class DtoGenerator : ISourceGenerator {
public void Execute(GeneratorExecutionContext context) {
foreach (var type in context.Compilation.SyntaxTrees) {
if (HasAttribute(type, "GenerateDTO")) {
var dtoCode = GenerateDtoClass(type);
context.AddSource($"{type.Name}Dto.cs", dtoCode);
}
}
}
}
.NET 6的剪裁功能可以移除未使用的代码:
xml复制<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
</PropertyGroup>
注意事项:反射调用的类型需要手动配置
<TrimmerRootAssembly>保留
C# 10的顶级语句看似简化,实则编译器仍会生成隐式类:
csharp复制// 用户编写的简单代码
Console.WriteLine("Hello");
// 实际生成的程序集结构
[CompilerGenerated]
internal static class <Program>$
{
private static void <Main>$(string[] args)
{
Console.WriteLine("Hello");
}
}