Why can I initialize a List like an array in C#?

前端 未结 6 1006
清歌不尽
清歌不尽 2020-12-12 23:20

Today I was surprised to find that in C# I can do:

List a = new List { 1, 2, 3 };

Why can I do this? What constructor

相关标签:
6条回答
  • 2020-12-12 23:38

    It is so called syntactic sugar.

    List<T> is the "simple" class, but compiler gives a special treatment to it in order to make your life easier.

    This one is so called collection initializer. You need to implement IEnumerable<T> and Add method.

    0 讨论(0)
  • 2020-12-12 23:38

    It works thanks to collection initializers which basically require the collection to implement an Add method and that will do the work for you.

    0 讨论(0)
  • 2020-12-12 23:44

    According to the C# Version 3.0 Specification "The collection object to which a collection initializer is applied must be of a type that implements System.Collections.Generic.ICollection for exactly one T."

    However, this information appears to be inaccurate as of this writing; see Eric Lippert's clarification in the comments below.

    0 讨论(0)
  • 2020-12-12 23:49

    This is part of the collection initializer syntax in .NET. You can use this syntax on any collection you create as long as:

    • It implements IEnumerable (preferably IEnumerable<T>)

    • It has a method named Add(...)

    What happens is the default constructor is called, and then Add(...) is called for each member of the initializer.

    Thus, these two blocks are roughly identical:

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

    And

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

    You can call an alternate constructor if you want, for example to prevent over-sizing the List<T> during growing, etc:

    // 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, }
    

    Note that the Add() method need not take a single item, for example the Add() method for Dictionary<TKey, TValue> takes two items:

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

    Is roughly identical to:

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

    So, to add this to your own class, all you need do, as mentioned, is implement IEnumerable (again, preferably IEnumerable<T>) and create one or more Add() methods:

    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();
        }
    }
    

    Then you can use it just like the BCL collections do:

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

    (For more information, see the MSDN)

    0 讨论(0)
  • 2020-12-12 23:51

    Another cool thing about collection initializers is that you can have multiple overloads of Add method and you can call them all in the same initializer! For example this works:

    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
    };
    

    It calls the correct overloads. Also, it looks for just the method with name Add, the return type could be anything.

    0 讨论(0)
  • 2020-12-12 23:56

    The array like syntax is being turned in a series of Add() calls.

    To see this in a much more interesting example, consider the following code in which I do two interesting things that sound first illegal in C#, 1) setting a readonly property, 2) setting a list with a array like initializer.

    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"}
    };
    

    This code will work perfectly, although 1) MyList is readonly and 2) I set a list with array initializer.

    The reason why this works, is because in code that is part of an object intializer the compiler always turns any {} like syntax to a series of Add() calls which are perfectly legal even on a readonly field.

    0 讨论(0)
提交回复
热议问题