【问题标题】:Casting generic class type to its base class fails [duplicate]将泛型类类型转换为其基类失败[重复]
【发布时间】:2018-03-10 06:30:42
【问题描述】:

我正在尝试将泛型类型转换为继承自它的非泛型类型,但它失败了。检查下面的代码:

    public class Subscription
    {
        public Subscription(DateTime expiration)
        {
            Expiration = expiration;
        }

        public DateTime Expiration { get; set; }
    }

    public class Subscription<T> : Subscription
    {
        public T Parameters { get; }

        public Subscription(DateTime expiration, T data)
            : base(expiration)
        {
            Parameters = data;
        }
    }


    public class SubscriptionsService
    {
        private readonly ConcurrentDictionary<int, object> subscriptions;

        public SubscriptionsService(ConcurrentDictionary<int, object> subscriptions)
        {
            this.subscriptions = subscriptions;
        }

        public void Add<T>(int clientId, DateTime expiration, T parameters)
        {
            if (!subscriptions.TryGetValue(clientId, out var outObject))
            {
                Subscription<T> subscription = new Subscription<T>(expiration, parameters);
                List<Subscription<T>> clientSubscriptions = new List<Subscription<T>>()
                {
                    subscription
                };

                subscriptions.AddOrUpdate(clientId, parameters, (k, v) => parameters);
            }
            else
            {
                if (outObject is List<Subscription<T>> resourceSubscriptions && resourceSubscriptions.Any())
                {
                    Subscription<T> subscription = resourceSubscriptions.SingleOrDefault(x => x.Parameters.Equals(parameters));
                    if (subscription == null)
                    {
                        subscription = new Subscription<T>(expiration, parameters);
                        resourceSubscriptions.Add(subscription);
                    }
                    else
                    {
                        subscription.Expiration = expiration;
                    }
                }
            }
        }

        public void Cleanup()
        {
            // Iterate through each reportId subscription
            foreach (KeyValuePair<int, object> subscriptionKeyValuePair in subscriptions)
            {
                List<Subscription> subscriptions = (subscriptionKeyValuePair.Value as List<Subscription>);
                if (subscriptions == null || !subscriptions.Any())
                    continue;

                foreach (var subscription in subscriptions)
                {
                   if (subscription.Expiration > DateTime.Now)
                   {
                      //some code
                   }
                }
            }
        }
    }

当我调用方法 Cleanup 并遍历键值对时,我试图将字典的值转换为我用来将其添加到 Add 方法的非泛型类型。我添加为 List> 并尝试将其转换为 List 以使用唯一的 Expiration 属性,因为我不需要 Cleanup 中的 T 参数。结果List&lt;Subscription&gt; subscriptions 强制转换失败并且 ```susbcriptions` 始终为 null,即使字典中的值不是。

【问题讨论】:

  • 为什么subscriptionsConcurrentDictionary&lt;int, object&gt;?不应该是ConcurrentDictionary&lt;int, Subscription&gt;吗?无论如何,List&lt;Subscription&lt;T&gt; 无法转换为 List&lt;Subscription&gt;
  • 只是one 的许多可能的重复
  • @ZoharPeled ConcurrentDictionary 不能这样,因为每个客户端都有很多订阅(List 并且每个都有不同的参数
  • Subscribtion&lt;T&gt; 转换为Subscription 是一回事,将List&lt;Subscription&lt;T&gt;&gt; 转换为List&lt;Subscription&gt; 是另一回事。整个讨论最终归结为以下演员,这是不可能的:List&lt;Base&gt; lst = new List&lt;Derived&gt;()

标签: c# .net generics


【解决方案1】:

您无法进行此转换,因为List&lt;T&gt; 中的T 是一个不变的类型参数。这意味着 C# 不会将 List&lt;A&gt; 视为 List&lt;B&gt; 的派生类型,即使 A 派生自 B

不过,IEnumerable&lt;T&gt; 有一个特殊功能,称为“协变类型参数”。它允许您进行这种类型的演员表。这意味着在下面的示例中,带有 list1 和 list2 的行将导致编译器错误,但 list3 可以正常工作。

public class Program<T>
{
    public void Main()
    {
        IList<Subscription> list1 = new List<Subscription<T>>();
        List<Subscription> list2 = new List<Subscription<T>>();
        IEnumerable<Subscription> list3 = new List<Subscription<T>>();
    }
}

public class Subscription<T> : Subscription
{
}

public class Subscription
{
}

有关协方差的更详细说明,请参阅https://docs.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance

【讨论】:

  • IEnumerable&lt;T&gt; 不是唯一这样的类型。还值得解释为什么它和其他类型,如 IReadOnlyList&lt;T&gt;Func&lt;TResult&gt; 可以安全地以这种方式定义,因为它绝不是任意的。
  • @natemcmaster 感谢您的回答。我之前尝试过协方差,但我避免了它,因为我想从原始列表中删除一个项目(因此代码if (subscription.Expiration &gt; DateTime.Now))。使用可枚举我无法删除它。
猜你喜欢
  • 2017-09-22
  • 1970-01-01
  • 1970-01-01
  • 2019-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-15
  • 1970-01-01
相关资源
最近更新 更多