如何以跨兼容方式扩展.NET枚举类型且不改动原有依赖类库?
解决方案
核心实现(基于.NET Standard 2.0 原生API)
核心思路是实现一个全局线程安全的枚举扩展注册中心,所有模块遵循该规范读写扩展格式信息即可,完全不需要修改原有类库代码。
第一步:定义格式元数据类
用于存储每个格式对应的名称、自定义属性等信息:
public sealed class ImageFormatMetadata { public string Name { get; } public IReadOnlyDictionary<string, object> CustomProperties { get; } public ImageFormatMetadata(string name, Dictionary<string, object> customProperties = null) { Name = name ?? throw new ArgumentNullException(nameof(name)); CustomProperties = customProperties ?? new Dictionary<string, object>(); } }
第二步:实现全局注册中心
采用静态类实现,进程内所有模块共享注册信息,内置重复注册校验:
public static class ImageFormatRegistry { private static readonly ConcurrentDictionary<ImageFormat, ImageFormatMetadata> _valueToMetadata = new(); private static readonly ConcurrentDictionary<string, ImageFormat> _nameToValue = new(StringComparer.OrdinalIgnoreCase); static ImageFormatRegistry() { // 初始化原生枚举值的映射 foreach (ImageFormat nativeValue in Enum.GetValues(typeof(ImageFormat))) { RegisterInternal(nativeValue, nativeValue.ToString(), skipDuplicateCheck: true); } } public static void Register(ImageFormat value, string name, Dictionary<string, object> customProperties = null) { RegisterInternal(value, name, skipDuplicateCheck: false, customProperties); } private static void RegisterInternal(ImageFormat value, string name, bool skipDuplicateCheck, Dictionary<string, object> customProperties = null) { if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("格式名称不能为空", nameof(name)); if (!skipDuplicateCheck) { if (_valueToMetadata.ContainsKey(value)) throw new InvalidOperationException($"ImageFormat值 {(int)value} 已被注册"); if (_nameToValue.ContainsKey(name)) throw new InvalidOperationException($"ImageFormat名称 {name} 已被注册"); } var metadata = new ImageFormatMetadata(name, customProperties); _valueToMetadata.TryAdd(value, metadata); _nameToValue.TryAdd(name, value); } public static bool TryGetMetadata(ImageFormat value, out ImageFormatMetadata metadata) { return _valueToMetadata.TryGetValue(value, out metadata); } public static bool TryParse(string formatName, out ImageFormat value) { return _nameToValue.TryGetValue(formatName, out value); } }
使用方式
扩展格式注册
各模块在初始化阶段注册自己的扩展格式即可,不需要和其他使用方模块交互:
// 模块启动时执行一次 public static void RegisterCustomFormats() { ImageFormatRegistry.Register( value: (ImageFormat)100003, name: "Webp", customProperties: new Dictionary<string, object> { ["MimeType"] = "image/webp", ["FileExtensions"] = new[] { ".webp" }, ["SupportTransparency"] = true }); }
格式信息读取
任意模块都可以直接读取全局注册的格式信息,不需要引用注册方的程序集:
// 从Image实例读取格式信息 if (ImageFormatRegistry.TryGetMetadata(image.Format, out var metadata)) { Console.WriteLine($"当前格式:{metadata.Name}"); Console.WriteLine($"对应的MIME类型:{metadata.CustomProperties["MimeType"]}"); } // 从名称解析格式 if (ImageFormatRegistry.TryParse("Webp", out var webpFormat)) { // 处理Webp格式逻辑 }
特性匹配验证
- 给定
ImageFormat值可直接获取名称和自定义属性,不需要反射/ToString:底层是字典查找,性能远高于反射 - 给定字符串可解析得到对应
ImageFormat值:内置TryParse方法直接实现 - 能力可被任意模块使用,单个模块修改全局可见:静态注册中心同AppDomain内全局共享
- 通信模块无需互相引用:所有模块仅依赖注册中心定义即可,注册方和使用方不需要直接引用
- 注册安全,重复注册可检测:注册方法内置值、名称的重复校验,异常信息明确
- 无随意性设计:核心就是全局映射存储,逻辑直白无隐藏规则
- 兼容.NET Standard 2.0:所有用到的API均在.NET Standard 2.0支持范围内
内容的提问来源于stack exchange,提问作者IS4
相关产品推荐
相关产品推荐

