C# 如何使用泛型参数类型作为接口的“嵌套”类型?

问题描述 投票:0回答:1

我只是在思考一些任务,并对一些基本概念产生了误解。这段代码

namespace Game {

    public static class EventSystem {

        public class EventBase { }

        public interface IEventListener<in TEvent> where TEvent : EventBase {
            void OnEvent(TEvent ev);
        }

        private static Dictionary<Type, List<IEventListener<EventBase>>> _subscriptions = new();

        public static void AddListener<TEvent>(IEventListener<TEvent> listener) where TEvent : EventBase {

            if (!_subscriptions.ContainsKey(typeof(TEvent))) {
                _subscriptions.Add(typeof(TEvent), new());
            }
            var list = _subscriptions[typeof(TEvent)];
            list.Add(listener);
        }

        public static void RemoveListener<TEvent>(IEventListener<TEvent> listener) where TEvent : EventBase {
            if (_subscriptions.ContainsKey(typeof(TEvent))) {
                var list = _subscriptions[typeof(TEvent)];
                list.Remove(listener);
            }
        }

在带有

list.Add(listener)
list.Remove(listener)
的行给我以下错误:

> Argument 1: cannot convert from
> 'Game.EventSystem.IEventListener<TEvent>' to
> 'Game.EventSystem.IEventListener<Game.EventSystem.EventBase>'CS1503

我正在尝试找出原因以及如何解决这个问题。 Cast (

list.Add((IEventListener<EventBase>)listener);
) 在运行时给我一个异常:System.InvalidCastException:指定的强制转换无效

所以,如果我正确理解了这种情况,

IEventListener<TEvent>
对于
EventBase
来说是不变的,尽管
TEvent
实际上是
EventBase
。我不明白的是‘为什么’?

PS。我知道可能存在内存泄漏,我们不讨论这个) - 只是想理解泛型类型参数有什么问题

c# generic-variance
1个回答
0
投票

关于文章创建变体通用接口(C#),你做不到。

作为可能的解决方案,您可以执行以下操作:

private static Dictionary<Type, object> _subscriptions = new();
    public static void AddListener<TEvent>(IEventListener<TEvent> listener) where TEvent : EventBase {

        if (!_subscriptions.TryGetValue(typeof(TEvent), out object? objList)) {
            Type listType = typeof(List<>).MakeGenericType(typeof(IEventListener<TEvent>));
            objList = Activator.CreateInstance(listType);
            _subscriptions.Add(typeof(TEvent), objList);
        }

        var list = (List<IEventListener<TEvent>>)objList;
        
        list.Add(listener);
    }

    public static void RemoveListener<TEvent>(IEventListener<TEvent> listener) where TEvent : EventBase {
        if (_subscriptions.ContainsKey(typeof(TEvent))) {
            var list = (List<IEventListener<TEvent>>)_subscriptions[typeof(TEvent)];
            list.Remove(listener);
        }
    }
© www.soinside.com 2019 - 2024. All rights reserved.