C#中Action<T>的唯一性判定与ConcurrentDictionary存储方案
解决方案
1. 自定义唯一标识键
先写一个ActionKey密封类,专门用来封装Action的唯一性信息,重写Equals和GetHashCode来精准匹配你的规则:
public sealed class ActionKey : IEquatable<ActionKey> { public object Target { get; } public MethodInfo Method { get; } private ActionKey(object target, MethodInfo method) { Target = target; Method = method; } public static ActionKey Create<T>(Action<T> action) { var method = action.Method; // 静态方法的Target是null,这时候用方法的声明类型当标识的一部分 var target = action.Target ?? method.DeclaringType; return new ActionKey(target, method); } public bool Equals(ActionKey other) { if (other is null) return false; // 先比实例/类型,再比方法,确保唯一 return ReferenceEquals(Target, other.Target) && Method.Equals(other.Method); } public override bool Equals(object obj) => Equals(obj as ActionKey); public override int GetHashCode() { // 把Target和Method的哈希值组合起来,大幅降低冲突概率 return HashCode.Combine(Target, Method); } }
逻辑很清晰:
- 实例方法:用具体对象实例+方法信息当标识,不同实例的同方法会被当成不同键
- 静态方法:用方法的声明类型+方法信息当标识,不管怎么创建Action,同一静态方法的键都是一样的,确保只存一次
2. 用非泛型ConcurrentDictionary存储
因为不同泛型参数的Action<T>没法直接塞进强类型泛型字典,所以用ConcurrentDictionary<ActionKey, Delegate>来存,所有泛型Action都能转成Delegate:
private readonly ConcurrentDictionary<ActionKey, Delegate> _actionStore = new(); public void RegisterAction<T>(Action<T> action) { var key = ActionKey.Create(action); // TryAdd是原子操作,键不存在才会添加成功,完美解决无重复存储 var isAdded = _actionStore.TryAdd(key, action); // 按需求输出valid: true if (isAdded) { Console.WriteLine($"valid: true > 已注册Action: {action.Method.Name}"); } else { Console.WriteLine($"valid: false > 重复Action: {action.Method.Name}"); } }
3. 测试验证
跑个测试看看规则是否生效:
public class DemoClass { public void InstanceMethod(int num) { } public static void StaticMethod(string str) { } } // 测试场景 var demo1 = new DemoClass(); var demo2 = new DemoClass(); // 不同实例的同实例方法,都能注册成功 RegisterAction<int>(demo1.InstanceMethod); // valid: true RegisterAction<int>(demo2.InstanceMethod); // valid: true // 同一静态方法多次注册,只有第一次成功 RegisterAction<string>(DemoClass.StaticMethod); // valid: true RegisterAction<string>(DemoClass.StaticMethod); // valid: false // 不同的匿名方法,即使逻辑类似也会被当成不同Action RegisterAction<int>(x => Console.WriteLine(x)); // valid: true RegisterAction<int>(x => Console.WriteLine(x * 2)); // valid: true
核心优势
- 解决泛型不兼容:用
Delegate作为值类型,兼容所有Action<T> - 精准唯一性:完全匹配你要的规则——实例方法按实例区分,静态方法全局唯一
- 线程安全:
ConcurrentDictionary的TryAdd是原子操作,并发场景下也不会出现重复存储
内容的提问来源于stack exchange,提问作者monty
相关产品推荐
相关产品推荐

