【问题标题】:Why is List<int> not IEnumerable<ValueType>?为什么 List<int> 不是 IEnumerable<ValueType>?
【发布时间】:2011-11-15 23:10:07
【问题描述】:

[编辑:我的歉意...最初的问题措辞含糊不清,我没有得到我正在寻找的答复]

对于任何继承自类 Y 的类 X,new List&lt;X&gt;() is IEnumerable&lt;Y&gt; 为真。但是,这不适用于结构:new List&lt;int&gt;() is IEnumerable&lt;ValueType&gt; 是错误的。我的问题是:为什么?

这是一个示例程序:

class Program
{
    class Y { }
    class X : Y { }
    struct Z { }

    static void Main(string[] args)
    {
        Test(new List<X>());
        Test(new List<string>());
        Test(new List<Z>());
        Test(new List<int>());
        Test("blah");
        Test(1);
        Console.ReadLine();
    }

    static void Test(object o)
    {
        if (o is IEnumerable<Y>)
        {
            Console.WriteLine(o + " is a list of Ys");
        }
        else if (o is IEnumerable<ValueType>)
        {
            Console.WriteLine(o + " is a list of ValueTypes");
        }
        else if (o is IEnumerable<object>)
        {
            Console.WriteLine(o + " is a list of objects");
        }
        else if (o is System.Collections.IEnumerable)
        {
            Console.WriteLine(o + " is most likely a list of ValueTypes or a string");
        }
        else
        {
            Console.WriteLine(o + " is not a list");
        }

    }
}

输出:

System.Collections.Generic.List`1[ConsoleApplication1.Program+X] 是 Ys 的列表

System.Collections.Generic.List`1[System.String] 是一个对象列表

System.Collections.Generic.List`1[ConsoleApplication1.Program+Z] 很可能是 ValueType 列表或字符串

System.Collections.Generic.List`1[System.Int32] 很可能是 ValueType 列表或字符串

blah 很可能是 ValueType 列表或字符串

1 不是列表

那么为什么new List&lt;int&gt; 不是IEnumerable&lt;ValueType&gt;

【问题讨论】:

  • 它们会导致错误的测试,因为它们是错误的。
  • @BenRobinson:为什么它们是假的?字符串数组不为假。 String 是一个对象,Int32 是一个 ValueType,它也是一个对象。是的,Int32 是一个结构体,但基数仍然是一个 ValueType
  • 是的,但是您没有检查 int 是否为 ValueType,您正在检查 lArr 是否为 IEnumerable&lt;ValueType&gt;List&lt;ValueType&gt; 而不是 List&lt;int&gt;
  • @BenRobinson:我认为你没有理解我的意思。如果 x 继承自 y,new List&lt;x&gt;() is y 应该为真。对于字符串和任何其他对象都是如此。对于 int 和任何其他 struct/ValueType 为 false。
  • 不,如果 x 继承自 y,我认为你错过了我的观点,那么 ListList&lt;x&gt; is List&lt;y&gt;() 对于任何类型都是不正确的

标签: c# .net


【解决方案1】:

协方差仅适用于引用类型,不适用于值类型。所以List&lt;string&gt; 可以分配给IEnumerable&lt;object&gt;,因为string 是一个引用类型,但是List&lt;int&gt; 不能分配给IEnumerable&lt;ValueType&gt;。有关详细信息,请参阅 C# 语言规范的第 13.1.3.2 节

【讨论】:

  • 谢谢。这就是我一直在寻找的。 “变体仅适用于引用类型;如果您为变体类型参数指定值类型,则该类型参数对于生成的构造类型是不变的。”来自[泛型中的协变和逆变][1] [1]:msdn.microsoft.com/en-us/library/dd799517.aspx
【解决方案2】:

iArr(以及随后的iArr[i])的类型是已知;编译器可以告诉您,您的表达式将始终返回给定值,因为它在编译时就知道答案。这里没有动态发生。

您实际上想在这里完成什么?为什么要测试int 是否派生自ValueType?当然可以;这是int!如果涉及泛型,这可能是有意义的(尽管您可能只使用约束),但不是在这里。

【讨论】:

  • 嗯......这只是一个例子。实际用例将是一个接受对象参数的函数,并且测试在对象上进行,结果相同。是的,警告很明显,但是为什么字符串数组比较没有警告呢? Int32 是一个 ValueType ...比较应该被视为类似于字符串数组比较
  • @Rado:在提出问题时,非常需要相关示例。你还没有告诉我们你实际上想要完成什么,你只问了如何解决剩下的 10% 的解决方案,这是没有任何意义的部分。
【解决方案3】:

在您的 cmets 中,如果 x 从 y 派生,您似乎期望 List&lt;x&gt;List&lt;y&gt; 派生,因此您期望某些检查为 true。对于List&lt;T&gt;,这是无效的假设。

如果x 类型与y 类型相关,您无法在不查看类型本身的情况下说出RandomGeneric&lt;x&gt;RandomGeneric&lt;y&gt; 之间的关系。

在您的特定情况下,List&lt;x&gt;List&lt;y&gt; 始终是同级类型(源自共同的父级,但彼此之间没有关系)。 IEnumerable&lt;x&gt;IEnumerable&lt;y&gt; 也是如此。

【讨论】:

    【解决方案4】:

    这个问题没有意义 iArr 中有值类型,因为它是一个整数数组。唯一在编译时不知道元素是什么类型的情况是,如果您有一个对象集合。在这种情况下,每个元素都可能是不同类型的对象,因此您必须检查每个元素。

    例如

    IEnumerable arse = SomeMethodThatReturnsIEnumerable();    
    bool areAllValueTypes = arse.<OfTypeobject>().All(x => x is ValueType);
    

    【讨论】:

      【解决方案5】:

      这取决于你的意思。

      var lst = new List<Object>();
      lst.Add(1);
      lst.Add(3);
      

      这是一个恰好只包含 ValueTypes 的对象集合。 这应该返回真还是假? 如果它应该返回 false,请使用:

      private bool attemptOne(IEnumerable coll)
      {
          var s = coll.GetType().GetGenericArguments()[0];
          return s.IsValueType;
      }
      
      var lstTwo = new List<int>();
      lstTwo.Add(1);
      lstTwo.Add(3);
      Console.WriteLine(attemptOne(lstTwo)); // Returns true
      

      如果它应该返回 true,请使用:

      private bool attemptTwo<T>(IEnumerable<T> coll)
      {
          return coll.All(c => c.GetType().IsValueType);
      }
      

      请注意,对于非常大的集合,第二种方式需要很长时间。

      【讨论】:

      • 你的 attemptTwo 方法有点毫无意义,你会在编译时知道它是否会在所有情况下返回 true,除了 T 是否是一个对象。最好有一个采用非泛型可枚举参数的非泛型方法。
      • @BenRobinson 是的,重点是如果 T 是一个对象,我什至指出了这一点
      【解决方案6】:

      我想我可以看到你在做什么

      bool IsCollectionOfValueTypes(Object argValue)
      {
        Type t = argValue.GetType();
        if(t.IsArray)
        {
          return t.GetElementType().IsValueType;
        }
        else
        {
          if (t.IsGeneric)
          {
            Types[] gt = t.GetGenericArguments();
             return (gt.Length == 1) && gt[0].IsValueType;
          }
        }
        return false;
      }
      

      如果您传入多维数组或构造类型,无论如何开始可能会变得更加复杂。不过,这一切都在课堂上的某个地方。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-07-01
        • 2012-01-25
        • 1970-01-01
        • 2013-11-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多