You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否将泛型委托存储在类型化列表中以避免强制转换?

嘿,我懂你现在的痛点——你用List<object>存储泛型委托,每次调用都得强制转换,想换成类型化列表来简化操作,但直接写List<dEvent<T>>总会报找不到T的错误,对吧?

为什么直接声明List<dEvent<T>>不行?

问题出在T的作用域上。你的dEvent<T>是泛型委托,但TestClass本身不是泛型类。当你在类里直接写List<dEvent<T>>时,编译器完全不知道这个T指代什么类型——它没有被定义为类的泛型参数,自然会抛出“未找到命名空间T”这类错误。

解决方案1:用Dictionary<Type, Delegate>按类型存储(推荐)

这种方法既能避免强制转换,又能提升调用时的效率(不用遍历整个列表),核心是用类型作为键,直接存储对应类型的泛型委托:

class TestClass 
{
    public delegate void dEvent<T>(object sender, T value);
    
    // 用字典按类型映射对应的泛型委托
    private Dictionary<Type, Delegate> _eventDictionary = new Dictionary<Type, Delegate>();

    public void Subscribe<T>(dEvent<T> ev)
    {
        var eventType = typeof(T);
        if (_eventDictionary.ContainsKey(eventType))
        {
            // 支持多个同类型委托,合并它们
            _eventDictionary[eventType] = Delegate.Combine(_eventDictionary[eventType], ev);
        }
        else
        {
            _eventDictionary.Add(eventType, ev);
        }
    }

    public void InvokeAll<T>(object sender, T value)
    {
        var eventType = typeof(T);
        if (_eventDictionary.TryGetValue(eventType, out var targetDelegate))
        {
            // 直接转换为对应泛型委托并调用
            (targetDelegate as dEvent<T>)?.Invoke(sender, value);
        }
    }

    static public void StringSubscriber(object sender, string value)
    {
        Debug.WriteLine(value);
    }

    static public void IntSubscriber(object sender, int value)
    {
        Debug.WriteLine(value);
    }

    static public void RunTest()
    {
        TestClass testClass = new TestClass();
        testClass.Subscribe<string>(StringSubscriber);
        testClass.Subscribe<int>(IntSubscriber);
        // 可以添加多个同类型委托测试合并效果
        // testClass.Subscribe<string>(sender => Debug.WriteLine("Another string handler!"));
        testClass.InvokeAll<string>(testClass, "Hello");
        testClass.InvokeAll<string>(null, "World");
        testClass.InvokeAll<int>(testClass, 4);
        testClass.InvokeAll<int>(null, 2);
    }
}

这个方案的优势很明显:

  • 调用时无需遍历整个列表,直接通过Type查找,效率更高
  • 避免了object列表的类型不安全问题
  • 支持多个同类型委托的订阅(通过Delegate.Combine合并)

解决方案2:用非泛型接口包装泛型委托

如果你的场景中委托数量不多,也可以通过定义非泛型接口来统一类型,实现类型化存储:

// 定义非泛型接口,统一调用入口
public interface IEventWrapper
{
    void Invoke(object sender, object value);
}

// 泛型包装类,将泛型委托适配到非泛型接口
public class EventWrapper<T> : IEventWrapper
{
    private readonly TestClass.dEvent<T> _targetEvent;

    public EventWrapper(TestClass.dEvent<T> ev)
    {
        _targetEvent = ev;
    }

    public void Invoke(object sender, object value)
    {
        if (value is T typedValue)
        {
            _targetEvent?.Invoke(sender, typedValue);
        }
    }
}

class TestClass 
{
    public delegate void dEvent<T>(object sender, T value);
    
    // 存储非泛型接口实例,实现类型化存储
    private List<IEventWrapper> _eventList = new List<IEventWrapper>();

    public void Subscribe<T>(dEvent<T> ev)
    {
        var wrapper = new EventWrapper<T>(ev);
        if (!_eventList.Contains(wrapper))
        {
            _eventList.Add(wrapper);
        }
    }

    public void InvokeAll<T>(object sender, T value)
    {
        foreach (var wrapper in _eventList)
        {
            wrapper.Invoke(sender, value);
        }
    }

    // 以下订阅方法和测试代码和你原来的一致
    static public void StringSubscriber(object sender, string value)
    {
        Debug.WriteLine(value);
    }

    static public void IntSubscriber(object sender, int value)
    {
        Debug.WriteLine(value);
    }

    static public void RunTest()
    {
        TestClass testClass = new TestClass();
        testClass.Subscribe<string>(StringSubscriber);
        testClass.Subscribe<int>(IntSubscriber);
        testClass.InvokeAll<string>(testClass, "Hello");
        testClass.InvokeAll<string>(null, "World");
        testClass.InvokeAll<int>(testClass, 4);
        testClass.InvokeAll<int>(null, 2);
    }
}

这个方案通过包装类将泛型委托转换为非泛型接口实例,列表存储接口对象,调用时自动处理类型转换,避免了直接存储object的麻烦。

内容的提问来源于stack exchange,提问作者dm76752

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:04:13