C# Smart Enums:高级
发布: (2026年1月2日 GMT+8 10:17)
4 min read
原文: Dev.to
Source: Dev.to

问题:复制粘贴陷阱
在第 2 部分,我们已经拥有了高性能的 O(1) 字典查找。然而,如果你的应用有数十种状态类型(订单、付款、用户等),你可能会发现自己一次又一次地复制粘贴相同的字典和查找逻辑。
建议:高级通用基类
为了保持代码 DRY(Don’t Repeat Yourself),我们可以将“繁重的工作”移到一个抽象基类中。这使得你的特定状态类能够专注于纯粹定义它们的值,同时免费继承所有优化的查找逻辑。
实现使用 Curiously Recurring Template Pattern (CRTP)。它确保每个特定的 Smart Enum(如 ProductStatus)在内存中维护自己的私有字典,防止不同类型之间的数据冲突。
实现
我们定义了一个简单的 interface,确保每个值都有一个 Id,随后提供一个基类 class,为安全访问数据提供多种方式。
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
public interface ISmartEnumValue
{
int Id { get; }
}
public abstract class SmartEnum
where TValue : class, ISmartEnumValue
where TSelf : SmartEnum
{
private static readonly Dictionary _lookup = new();
protected static TValue Register(TValue value)
{
_lookup[value.Id] = value;
return value;
}
// Forces the static constructor of the child class to run immediately
public static void Initialize()
{
RuntimeHelpers.RunClassConstructor(typeof(TSelf).TypeHandle);
}
// 1. Strict Get: Throws if ID is missing (Use when existence is mandatory)
public static TValue Get(int id)
{
if (_lookup.TryGetValue(id, out var value))
return value;
throw new KeyNotFoundException(
$"Value with ID {id} not found in {typeof(TSelf).Name}");
}
// 2. Safe Get: Returns null if ID is missing (Use for optional data)
public static TValue? GetOrDefault(int id) => _lookup.GetValueOrDefault(id);
// 3. Pattern Matching Get: Returns bool (Standard .NET 'Try' pattern)
public static bool TryGet(int id, out TValue? value)
{
return _lookup.TryGetValue(id, out value);
}
public static bool Exists(int id) => _lookup.ContainsKey(id);
public static IEnumerable GetAll() => _lookup.Values;
}
使用示例
初始化后,您可以完全灵活地使用您的 Smart Enums。
// Initialize once at startup (e.g., Program.cs)
ProductStatus.Initialize();
// Example 1: Strict access (expects ID to exist)
var status = ProductStatus.Get(1);
Console.WriteLine(status.Description);
// Example 2: Safe access with null check
var maybeStatus = ProductStatus.GetOrDefault(99);
if (maybeStatus != null) { /* Do something */ }
// Example 3: Pattern matching for clean branching
if (ProductStatus.TryGet(2, out var foundStatus))
{
Console.WriteLine($"Found: {foundStatus?.Description}");
}
为什么这是一个健壮的架构选择
- 灵活的使用 – 根据业务流程,在异常、null 或布尔值之间进行选择。
- 严格的类型安全 –
TSelf约束确保ProductStatus.Get()直接返回ProductStatusValue,无需强制转换。 - 无需反射 – 通过使用
Initialize(),我们避免了程序集扫描的性能开销。 - 零样板代码 – 具体的枚举类专注于数据本身,而引擎则封装在基类中。
动手尝试
版本说明
此高级实现需要 .NET 6 或更高版本。使用 CRTP 和现代泛型约束可确保在现代 C# 环境中实现类型安全和性能。
进一步阅读与资源
- Microsoft Docs: Generic Constraints
- Ardalis.SmartEnum: GitHub Repository – 业界标准的生产级智能枚举。