【问题标题】:In C#, why can't a List<string> object be stored in a List<object> variable在 C# 中,为什么不能将 List<string> 对象存储在 List<object> 变量中
【发布时间】:2008-08-09 02:32:01
【问题描述】:

似乎 List 对象不能存储在 C# 中的 List 变量中,甚至不能以这种方式显式转换。

List<string> sl = new List<string>();
List<object> ol;
ol = sl;

导致无法将类型 System.Collections.Generic.List&lt;string&gt; 隐式转换为 System.Collections.Generic.List&lt;object&gt;

然后……

List<string> sl = new List<string>();
List<object> ol;
ol = (List<object>)sl;

导致无法将类型 System.Collections.Generic.List&lt;string&gt; 转换为 System.Collections.Generic.List&lt;object&gt;

当然,您可以通过从字符串列表中拉出所有内容并一次将其放回其中来做到这一点,但这是一个相当复杂的解决方案。

【问题讨论】:

标签: c# .net generics covariance type-safety


【解决方案1】:

这样想,如果你要进行这样的转换,然后将 Foo 类型的对象添加到列表中,则字符串列表不再一致。如果你要迭代第一个引用,你会得到一个类转换异常,因为一旦你点击 Foo 实例,Foo 就无法转换为字符串!

作为旁注,我认为是否可以进行反向转换会更重要:

List<object> ol = new List<object>();
List<string> sl;
sl = (List<string>)ol;

我有一段时间没有使用 C#,所以我不知道这是否合法,但这种转换实际上(可能)有用。在这种情况下,您将从一个更通用的类(对象)转到一个从通用类扩展的更具体的类(字符串)。这样,如果你添加到字符串列表中,你并没有违反对象列表。

有人知道或可以测试这种转换在 C# 中是否合法吗?

【讨论】:

  • 我认为您的示例遇到了同样的问题。如果 ol 中有一些不是字符串的东西会发生什么?问题是 List 上的某些方法可以正常工作,例如添加/插入。但迭代可能是一个真正的问题。
  • Eric Lippert 有一系列关于这个主题的精彩博客文章:为什么向泛型方法添加协变和逆变约束可能会起作用,但在类级别可能永远不会像我们希望的那样起作用。 is.gd/3kQc
  • @ChrisAmmerman:如果ol 里面有一些不是字符串的东西,我想我希望演员阵容在运行时会失败。但是你真正会遇到麻烦的地方是如果演员成功,并且 then 一些东西被添加到ol 这不是一个字符串。因为sl 引用了同一个对象,所以现在你的List&lt;string&gt; 将包含一个非字符串。 Add 是问题所在,我想这可以解释为什么这段代码无法编译,但如果你将 List&lt;object&gt; ol 更改为 IEnumerable&lt;object&gt; ol,它会编译,而 IEnumerable&lt;object&gt; ol 没有 Add。 (我在 C# 4 中检查了这一点。)
  • 进行该更改后,它会编译但会抛出InvalidCastException,因为ol 的运行时类型仍然是List&lt;object&gt;
【解决方案2】:

如果您使用的是 .NET 3.5,请查看 Enumerable.Cast 方法。它是一种扩展方法,因此您可以直接在 List 上调用它。

List<string> sl = new List<string>();
IEnumerable<object> ol;
ol = sl.Cast<object>();

这不完全符合您的要求,但应该可以解决问题。

编辑:如 Zooba 所述,您可以调用 ol.ToList() 来获取列表

【讨论】:

    【解决方案3】:

    您不能在具有不同类型参数的泛型类型之间进行转换。专门的泛型类型不构成同一继承树的一部分,不相关的类型也是如此。

    在 NET 3.5 之前执行此操作:

    List<string> sl = new List<string>();
    // Add strings to sl
    
    List<object> ol = new List<object>();
    
    foreach(string s in sl)
    {
        ol.Add((object)s);  // The cast is performed implicitly even if omitted
    }
    

    使用 Linq:

    var sl = new List<string>();
    // Add strings to sl
    
    var ol = new List<object>(sl.Cast<object>());
    
    // OR
    var ol = sl.Cast<object>().ToList();
    
    // OR (note that the cast to object here is required)
    var ol = sl.Select(s => (object)s).ToList();
    

    【讨论】:

      【解决方案4】:

      原因是像List&lt;&gt; 这样的泛型类在大多数情况下在外部被视为普通类。例如当您说List&lt;string&gt;() 时,编译器会说ListString()(其中包含字符串)。 [技术人员:这是正在发生的事情的非常简单的英语版本]

      因此,显然编译器不够聪明,无法通过转换其内部集合的项目将 ListString 转换为 ListObject。

      这就是为什么有像 Convert() 这样的 IEnumerable 扩展方法,它允许您轻松地为存储在集合中的项目提供转换,这就像从一个到另一个的转换一样简单。

      【讨论】:

        【解决方案5】:

        这与协方差有很大关系,例如,泛型类型被视为参数,如果参数不能正确解析为更具体的类型,则操作失败。这意味着你真的不能转换为更一般的类型,比如对象。正如 Rex 所说,List 对象不会为您转换每个对象。

        您可能想尝试使用 ff 代码:

        List<string> sl = new List<string>();
        //populate sl
        List<object> ol = new List<object>(sl);
        

        或:

        List<object> ol = new List<object>();
        ol.AddRange(sl);
        

        ol (理论上)会毫无问题地复制 sl 的所有内容。

        【讨论】:

          【解决方案6】:

          是的,您可以,从 .NET 3.5 开始:

          List<string> sl = new List<string>();
          List<object> ol = sl.Cast<object>().ToList();
          

          【讨论】:

            【解决方案7】:

            Mike - 我相信 C# 中也不允许逆变

            有关更多信息,请参阅Generic type parameter variance in the CLR

            【讨论】:

              【解决方案8】:

              【讨论】:

                【解决方案9】:

                这实际上是为了让您不要尝试在您的“ol”列表变体中放置任何奇怪的“对象”(List&lt;object&gt; 似乎允许)-因为您的代码会崩溃(因为列表确实是 @ 987654322@ 并且只接受 String 类型的对象)。这就是为什么您不能将变量转换为更通用的规范。

                在 Java 上则相反,您没有泛型,而是在运行时一切都是对象列表,您真的可以将任何奇怪的对象填充到您所谓的严格类型列表中。搜索“Reified generics”以查看有关 java 问题的更广泛讨论...

                【讨论】:

                  【解决方案10】:

                  不支持对泛型的这种协方差,但您实际上可以使用数组来做到这一点:

                  object[] a = new string[] {"spam", "eggs"};
                  

                  C# 执行运行时检查以防止您将int 放入a

                  【讨论】:

                    【解决方案11】:

                    这是任何 IList 的另一个 pre-.NET 3.5 解决方案,其内容可以隐式转换。

                    public IList<B> ConvertIList<D, B>(IList<D> list) where D : B
                    {
                        List<B> newList = new List<B>();
                    
                        foreach (D item in list)
                        {
                            newList.Add(item);
                        }
                    
                        return newList;
                    }
                    

                    (以 Zooba 为例)

                    【讨论】:

                      【解决方案12】:

                      我有一个:

                      private List<Leerling> Leerlingen = new List<Leerling>();
                      

                      我打算用List&lt;object&gt; 中收集的数据填充它 最终对我有用的是这个:

                      Leerlingen = (List<Leerling>)_DeserialiseerLeerlingen._TeSerialiserenObjecten.Cast<Leerling>();
                      

                      .Cast 将其转换为您想要从该类型获取 IEnumerable 的类型,然后将 IEnemuerable 类型转换为您想要的 List&lt;&gt;

                      【讨论】:

                        【解决方案13】:

                        嗯,感谢之前的 cmets,我找到了两种方法来找出它。第一个是获取元素的字符串列表,然后将其转换为 IEnumerable 对象列表:

                        IEnumerable<object> ob;
                        List<string> st = new List<string>();
                        ob = st.Cast<object>();
                        

                        第二个是避免IEnumerable对象类型,只是将字符串转换为对象类型,然后在同一句话中使用函数“toList()”:

                        List<string> st = new List<string>();
                        List<object> ob = st.Cast<object>().ToList();
                        

                        我更喜欢第二种方式。我希望这会有所帮助。

                        【讨论】:

                          【解决方案14】:
                          List<string> sl = new List<string>();
                          List<object> ol;
                          ol = new List<object>(sl);
                          

                          【讨论】:

                            猜你喜欢
                            • 1970-01-01
                            • 2014-04-04
                            • 2018-11-23
                            • 2021-08-08
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            相关资源
                            最近更新 更多