它是如何工作的。在linq中的字典?

时间:2017-10-23 20:11:47

标签: c# .net linq dictionary key-value

在这种情况下我有错误,Visual Studio说unreadDialogs.key有另一个(TLAbsPeer)类型,

var unreadDialogs = dialogs.dialogs.lists
               .Where(t => t.unread_count > 0 && t.peer.GetType() == typeof(TLPeerChannel))
               .ToDictionary(t => t.peer, t => t.unread_count);
            foreach (KeyValuePair <TLPeerChannel,int> channel in unreadDialogs)
            {  }

但是当我写这篇文章时,一切都好。

 var unreadDialogs = dialogs.dialogs.lists
               .Where(t => t.unread_count > 0 && t.peer.GetType() == typeof(TLPeerChannel))
               .Select(t => t.peer);
            foreach (TLPeerChannel channel in unreadDialogs)
            {  }

为什么?

P.S peer可以是TLPeerChannel或TLPeerUser

这是a fiddle that demonstrates the problem

2 个答案:

答案 0 :(得分:0)

您必须区分静态类型(即编译时类型)和运行时类型。例如,t.peer可以静态类型为object,但在运行时包含对TLPeerChannel对象的引用。如果是这种情况,那么t => t.peer将创建一个密钥类型为object的字典,无论运行时类型如何,因为密钥的类型是在编译时确定的。

您可以将其更改为t => (TLPeerChannel)t.peer

var unreadDialogs = dialogs.dialogs.lists
    .Where(t => t.unread_count > 0 && t.peer.GetType() == typeof(TLPeerChannel))
    .ToDictionary(t => (TLPeerChannel)t.peer, t => t.unread_count);
foreach (KeyValuePair <TLPeerChannel,int> channel in unreadDialogs)
{  }

答案 1 :(得分:-1)

因为,在使用ToDictionary构建字典时,它会将创建的字典的键键入t.peer的类型,而不是TLPeerChannel。我推测t.peer的类型是基本类型,可以是TLPeerChannelTLPeerUser。因此,当您尝试使用类型为KeyValuePair的密钥TLPeerChannel访问该字典时,这不是字典中密钥的类型。

你可以通过强制转换为你要使用的类型来解决它......

var unreadDialogs = dialogs.dialogs.lists
           .Where(t => t.unread_count > 0 && t.peer.GetType() == typeof(TLPeerChannel))
           .ToDictionary(t => ((TLPeerChannel) t.peer), t => t.unread_count);
        foreach (KeyValuePair <TLPeerChannel,int> channel in unreadDialogs)
        {  }

告诉ToDictionary方法,字典键的类型是所有键的类型,是TLPeerChannel。

问题与协方差和逆变以及隐式转换是否可证明是安全的有关。在您的第一个代码中,编译器不能假设从TLAbsPeer转换为TLPeerChannel是可证明安全的。 Eric Lippert更好地解释了这一点并提供了一些参考文献 here