【问题标题】:Why can I initialize a List like an array in C#?为什么我可以像 C# 中的数组一样初始化 List?
【发布时间】:2012-01-13 16:35:08
【问题描述】:

今天我惊讶地发现在 C# 中我可以做到:

List<int> a = new List<int> { 1, 2, 3 };

为什么我可以这样做?调用什么构造函数?我怎样才能用我自己的课程做到这一点?我知道这是初始化数组的方法,但数组是语言项,列表是简单对象...

【问题讨论】:

  • 这个问题可能会有所帮助:stackoverflow.com/questions/1744967/…
  • 非常棒吧?您也可以执行非常相似的代码来初始化字典:{ { "key1", "value1"}, { "key2", "value2"} }
  • 从另一个角度看,这个类似数组的初始化语法提醒我们List&lt;int&gt; 的底层数据类型实际上只是一个数组。我讨厌 C# 团队,因为他们没有将其命名为 ArrayList&lt;T&gt;,这听起来如此明显和自然。

标签: c# .net list initialization


【解决方案1】:

这是 .NET 中集合初始值设定项语法的一部分。您可以在您创建的任何集合上使用此语法,只要:

  • 它实现了IEnumerable(最好是IEnumerable&lt;T&gt;

  • 它有一个名为Add(...)的方法

调用默认构造函数,然后为初始化程序的每个成员调用Add(...)

因此,这两个块大致相同:

List<int> a = new List<int> { 1, 2, 3 };

List<int> temp = new List<int>();
temp.Add(1);
temp.Add(2);
temp.Add(3);
List<int> a = temp;

如果需要,您可以调用备用构造函数,例如防止在增长过程中过度调整 List&lt;T&gt; 的大小等:

// Notice, calls the List constructor that takes an int arg
// for initial capacity, then Add()'s three items.
List<int> a = new List<int>(3) { 1, 2, 3, }

请注意,Add() 方法不必采用单个项目,例如 Dictionary&lt;TKey, TValue&gt;Add() 方法采用两个项目:

var grades = new Dictionary<string, int>
    {
        { "Suzy", 100 },
        { "David", 98 },
        { "Karen", 73 }
    };

大致等同于:

var temp = new Dictionary<string, int>();
temp.Add("Suzy", 100);
temp.Add("David", 98);
temp.Add("Karen", 73);
var grades = temp;

因此,要将它添加到您自己的类中,您需要做的就是实现IEnumerable(同样,最好是IEnumerable&lt;T&gt;)并创建一个或多个Add() 方法:

public class SomeCollection<T> : IEnumerable<T>
{
    // implement Add() methods appropriate for your collection
    public void Add(T item)
    {
        // your add logic    
    }

    // implement your enumerators for IEnumerable<T> (and IEnumerable)
    public IEnumerator<T> GetEnumerator()
    {
        // your implementation
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

然后你可以像 BCL 集合一样使用它:

public class MyProgram
{
    private SomeCollection<int> _myCollection = new SomeCollection<int> { 13, 5, 7 };    

    // ...
}

(有关详细信息,请参阅MSDN

【讨论】:

  • 很好的答案,尽管我注意到在第一个示例中说“完全相同”并不十分准确。它与List&lt;int&gt; temp = new List&lt;int&gt;(); temp.Add(1); ... List&lt;int&gt; a = temp; 完全相同,即a 变量直到 调用所有添加之后才被初始化。否则,做类似List&lt;int&gt; a = new List&lt;int&gt;() { a.Count, a.Count, a.Count }; 这样的事情是合法的,这是一件很疯狂的事情。
  • @user606723:List&lt;int&gt; a; a = new List&lt;int&gt;() { a.Count };List&lt;int&gt; a = new List&lt;int&gt;() { a.Count }; 之间没有任何真正的区别
  • @Joren:你是对的;事实上,C# 规范声明T x = y;T x; x = y; 相同,这一事实可能会导致一些奇怪的情况。例如,int x = M(out x) + x; 是完全合法的,因为 int x; x = M(out x) + x; 是合法的。
  • @JamesMichaelHare 没有必要实现IEnumerable&lt;T&gt;;非泛型 IEnumerable 足以允许使用集合初始化语法。
  • 如果您使用某种实现 IDisposable 的集合,Eric 的观点很重要。如果Add 调用之一失败,using (var x = new Something{ 1, 2 }) 将不会释放对象。
【解决方案2】:

就是所谓的syntactic sugar

List&lt;T&gt; 是“简单”类,但编译器对其进行了特殊处理以使您的生活更轻松。

这就是所谓的collection initializer。您需要实现IEnumerable&lt;T&gt;Add 方法。

【讨论】:

    【解决方案3】:

    根据C# Version 3.0 Specification“应用集合初始化器的集合对象必须是为恰好一个 T 实现 System.Collections.Generic.ICollection 的类型。”

    但是,在撰写本文时,此信息似乎不准确;请参阅以下 cmets 中 Eric Lippert 的说明。

    【讨论】:

    • 该页面不准确。感谢您引起我的注意。那一定是一些从未被删除的初步预发布文档。最初的设计是需要 ICollection,但这不是我们确定的最终设计。
    【解决方案4】:

    这要归功于collection initializers,它基本上需要集合来实现 Add 方法,这将为您完成工作。

    【讨论】:

      【解决方案5】:

      关于集合初始化器的另一个很酷的事情是你可以有多个Add 方法的重载,你可以在同一个初始化器中调用它们!例如这有效:

      public class MyCollection<T> : IEnumerable<T>
      {
          public void Add(T item, int number)
          {
      
          }
          public void Add(T item, string text) 
          {
      
          }
          public bool Add(T item) //return type could be anything
          {
      
          }
      }
      
      var myCollection = new MyCollection<bool> 
      {
          true,
          { false, 0 },
          { true, "" },
          false
      };
      

      它调用正确的重载。此外,它只查找名称为 Add 的方法,返回类型可以是任何类型。

      【讨论】:

        【解决方案6】:

        类似数组的语法正在一系列Add() 调用中转换。

        要在一个更有趣的示例中看到这一点,请考虑以下代码,其中我做了两件有趣的事情,这在 C# 中听起来首先是非法的,1) 设置只读属性,2) 设置带有数组的列表,如初始化程序。

        public class MyClass
        {   
            public MyClass()
            {   
                _list = new List<string>();
            }
            private IList<string> _list;
            public IList<string> MyList 
            { 
                get
                { 
                    return _list;
                }
            }
        }
        //In some other method
        var sample = new MyClass
        {
            MyList = {"a", "b"}
        };
        

        尽管 1) MyList 是只读的,并且 2) 我使用数组初始化器设置了一个列表,但这段代码将完美运行。

        之所以可行,是因为在作为对象初始化程序一部分的代码中,编译器总是将任何类似{} 的语法转换为一系列Add() 调用,即使在只读字段上也是完全合法的。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-02-09
          • 1970-01-01
          • 1970-01-01
          • 2020-03-20
          • 2023-03-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多