C#中如何获取类声明的泛型类型以验证委托签名实现信号系统
C#类型安全信号系统的实现优化
我要在C#中实现一套编译阶段即可完成信号与委托验证的类型安全信号系统,设定了三个优先级需求:
- 类型安全:开发者不得向预定义信号的监听器传递错误参数类型,也不能注册签名不匹配的函数。
- 语法简洁:创建新信号、注册/注销监听器、触发信号的操作需快捷简便,无冗余代码负担。
- 参数描述性:触发信号时,参数名称需体现其在该信号中的具体用途,例如使用
(string userName, int userAge)而非(string param1, int param2)。
目前前两项需求已实现,但第三项尚未达成,同时我对现有实现的语法冗余问题也不满意。
当前实现方案
信号基类(需按泛型参数数量重复实现,如SigClass<T,U>、SigClass<T,U,V>等)
public delegate void Callback<T>(T arg1); public class SigClass<T> { protected static Dictionary<string, Delegate> _event = new Dictionary<string, Delegate>(); public static void Send<C>(T p1) where C : SCSigBase { string s = typeof(C).Name; if (!_event.ContainsKey(s)) { _event.Add(s, null);} Callback<T> callback = _event[s] as Callback<T>; callback?.Invoke(p1); } public static void Register<C>(Callback<T> function) where C : SCSigBase { string s = typeof(C).Name; if (!_event.ContainsKey(s)) { _event.Add(s, null);} _event[s] = (Callback<T>)_event[s] + function; } public static void Unregister<C>(Callback<T> function) where C : SCSigBase { string s = typeof(C).Name; if (!_event.ContainsKey(s)) { _event.Add(s, null);} _event[s] = (Callback<T>)_event[s] - function; } }
声明新信号类型
public class FooSig : SigClass<string> {}
注册/注销监听器、触发信号
public void RunTests() { FooSig.Register<FooSig>(Bar); FooSig.Send<FooSig>("Hello, world!"); FooSig.Unregister<FooSig>(Bar); FooSig.Send<FooSig>("You should not see this."); } public void Bar(string message) { Console.Write(message); }
此处的泛型参数<C>仅用于避免相同泛型参数的信号冲突,属于冗余代码,我希望将其优化掉。
优化尝试与核心问题
我想将调用逻辑简化为以下形式:不再通过继承类(如FooSig)持有方法和静态字典,而是使用一个静态类SignalManager,通过传入信号类型完成验证:
public void RunTests() { SignalManager.Register<FooSig>(Bar); SignalManager.Send<FooSig>("Hello, world!"); SignalManager.Unregister<FooSig>(Bar); SignalManager.Send<FooSig>("You should not see this."); }
现在的核心问题是:
是否存在方法可以获取类声明时使用的泛型类型,并利用该类型验证传递的委托签名?
换句话说,当我声明FooSig : SigClass<string>{}时,能否在验证委托签名时复用这个string泛型类型?
期望实现的编译期验证效果
我希望代码能达到以下编译阶段的校验效果:
public class FooSig : SigClass<string>{} public void RunTests() { SignalManager.Register<FooSig>(Bar); SignalManager.Register<FooSig>(Baz); // 此代码应编译失败,Baz的签名与FooSig的泛型类型不匹配 SignalManager.Send<FooSig>("Hello, world!"); SignalManager.Send<FooSig>(); // 此代码应编译失败,FooSig要求传入string类型参数 } public void Bar(string param) { // 执行操作 } public void Baz(int param) { // 执行操作 }
内容的提问来源于stack exchange,提问作者doktorjake
相关产品推荐
相关产品推荐

