物件和集合初始設定式 (C# 程式設計手冊)Object and Collection Initializers (C# Programming Guide)

C# 可讓您具現化物件或集合,並以單一陳述式執行成員指派。C# lets you instantiate an object or collection and perform member assignments in a single statement.

物件初始設定式Object initializers

物件初始設定式可讓您在建立期間將值指派給物件的任何可存取欄位或屬性,而不用叫用後面接著幾行指派陳述式的建構函式。Object initializers let you assign values to any accessible fields or properties of an object at creation time without having to invoke a constructor followed by lines of assignment statements. 物件初始設定式語法可讓您為建構函式指定引數或省略引數 (以及括號語法)。The object initializer syntax enables you to specify arguments for a constructor or omit the arguments (and parentheses syntax). 下列範例將示範如何使用有具名型別 Cat 的物件初始設定式,以及如何叫用無參數建構函式。The following example shows how to use an object initializer with a named type, Cat and how to invoke the parameterless constructor. 請注意 Cat 類別中自動實作屬性的用法。Note the use of auto-implemented properties in the Cat class. 如需詳細資訊,請參閱自動實作的屬性For more information, see Auto-Implemented Properties.

public class Cat
{
    // Auto-implemented properties.
    public int Age { get; set; }
    public string Name { get; set; }

    public Cat()
    {
    }

    public Cat(string name)
    {
        this.Name = name;
    }
}
Cat cat = new Cat { Age = 10, Name = "Fluffy" };
Cat sameCat = new Cat("Fluffy"){ Age = 10 };

物件初始設定式語法可讓您建立執行個體,然後將新建立的物件及其指派的屬性指派給指派作業中的變數。The object initializers syntax allows you to create an instance, and after that it assigns the newly created object, with its assigned properties, to the variable in the assignment.

從 C# 6 開始,物件初始設定式除了指派欄位和屬性,還可以設定索引子。Starting with C# 6, object initializers can set indexers, in addition to assigning fields and properties. 請考慮此基本 Matrix 類別:Consider this basic Matrix class:

public class Matrix
{
    private double[,] storage = new double[3, 3];

    public double this[int row, int column]
    {
        // The embedded array will throw out of range exceptions as appropriate.
        get { return storage[row, column]; }
        set { storage[row, column] = value; }
    }
}

您可以使用下列程式碼來初始化單位矩陣:You could initialize the identity matrix with the following code:

var identity = new Matrix
{
    [0, 0] = 1.0,
    [0, 1] = 0.0,
    [0, 2] = 0.0,

    [1, 0] = 0.0,
    [1, 1] = 1.0,
    [1, 2] = 0.0,

    [2, 0] = 0.0,
    [2, 1] = 0.0,
    [2, 2] = 1.0,
};

任何包含可存取 setter 的可存取索引子都可作為物件初始設定式中的其中一個運算式,不論引數的數目或類型為何。Any accessible indexer that contains an accessible setter can be used as one of the expressions in an object initializer, regardless of the number or types of arguments. 指派左側是由索引引數組成,運算式右側是其值。The index arguments form the left side of the assignment, and the value is the right side of the expression. 例如,如果 IndexersExample 具有適當的索引子,下列內容全部有效:For example, these are all valid if IndexersExample has the appropriate indexers:

var thing = new IndexersExample {
    name = "object one",
    [1] = '1',
    [2] = '4',
    [3] = '9',
    Size = Math.PI,
    ['C',4] = "Middle C"
}

若要編譯上述程式碼,IndexersExample 類型必須具有下列成員:For the preceding code to compile, the IndexersExample type must have the following members:

public string name;
public double Size { set { ... }; }
public char this[int i] { set { ... }; }
public string this[char c, int i] {  set { ... }; }

具有匿名類型的物件初始設定式Object Initializers with anonymous types

雖然物件初始設定式可以用於任何內容,但是在 LINQLINQ 查詢運算式中特別有用。Although object initializers can be used in any context, they are especially useful in LINQLINQ query expressions. 查詢運算式經常使用匿名型別,此型別只能使用物件初始設定式初始化,如下列宣告所示。Query expressions make frequent use of anonymous types, which can only be initialized by using an object initializer, as shown in the following declaration.

var pet = new { Age = 10, Name = "Fluffy" };  

匿名型別可讓 LINQLINQ 查詢運算式中的 select 子句將原始序列的物件轉換為其值和圖形可能與原始物件不同的物件。Anonymous types enable the select clause in a LINQLINQ query expression to transform objects of the original sequence into objects whose value and shape may differ from the original. 如果您只要儲存序列中每個物件的部分資訊,這就會很實用。This is useful if you want to store only a part of the information from each object in a sequence. 在下列範例中,假設產品物件 (p) 包含許多欄位和方法,而您只想要建立包含產品名稱和單價的物件序列。In the following example, assume that a product object (p) contains many fields and methods, and that you are only interested in creating a sequence of objects that contain the product name and the unit price.

var productInfos =
    from p in products
    select new { p.ProductName, p.UnitPrice };

執行此查詢時,productInfos 變數將會包含可以在 foreach 陳述式中存取的物件序列,如下列範例所示:When this query is executed, the productInfos variable will contain a sequence of objects that can be accessed in a foreach statement as shown in this example:

foreach(var p in productInfos){...}  

新的匿名型別中的每個物件都有兩個公用屬性,這兩個屬性會接收與原始物件中的屬性或欄位相同的名稱。Each object in the new anonymous type has two public properties that receive the same names as the properties or fields in the original object. 您也可以在建立匿名類型時重新命名欄位,下列範例會將 UnitPrice 欄位重新命名為 PriceYou can also rename a field when you are creating an anonymous type; the following example renames the UnitPrice field to Price.

select new {p.ProductName, Price = p.UnitPrice};  

集合初始設定式Collection initializers

如果集合類別是實作 IEnumerable,且具有 Add 與適當簽章以作為執行個體方法或擴充方法,集合初始設定式可讓您在初始化這類集合類別時,指定一或多個項目初始設定式。Collection initializers let you specify one or more element initializers when you initialize a collection type that implements IEnumerable and has Add with the appropriate signature as an instance method or an extension method. 項目初始設定式可以是簡單的值、運算式或物件初始設定式。The element initializers can be a simple value, an expression, or an object initializer. 藉由使用集合初始設定式,您就不需要指定多個呼叫,編譯器會自動新增呼叫。By using a collection initializer, you do not have to specify multiple calls; the compiler adds the calls automatically.

下列範例將示範兩個簡單的集合初始設定式:The following example shows two simple collection initializers:

List<int> digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };  
List<int> digits2 = new List<int> { 0 + 1, 12 % 3, MakeInt() };  

下列集合初始設定式會使用物件初始設定式來初始化先前範例中所定義 Cat 類別的物件。The following collection initializer uses object initializers to initialize objects of the Cat class defined in a previous example. 請注意,每個物件初始設定式會以括號括住並以逗號分隔。Note that the individual object initializers are enclosed in braces and separated by commas.

List<Cat> cats = new List<Cat>
{
    new Cat{ Name = "Sylvester", Age=8 },
    new Cat{ Name = "Whiskers", Age=2 },
    new Cat{ Name = "Sasha", Age=14 }
};

如果集合的 Add 方法允許,您可以將 null 指定為集合初始設定式中的項目。You can specify null as an element in a collection initializer if the collection's Add method allows it.

List<Cat> moreCats = new List<Cat>
{
    new Cat{ Name = "Furrytail", Age=5 },
    new Cat{ Name = "Peaches", Age=4 },
    null
};

如果集合支援讀取/寫入索引,您可以指定索引的項目。You can specify indexed elements if the collection supports read / write indexing.

var numbers = new Dictionary<int, string>
{
    [7] = "seven",
    [9] = "nine",
    [13] = "thirteen"
};

上述範例會產生呼叫 Item[TKey] 以設定值的程式碼。The preceding sample generates code that calls the Item[TKey] to set the values. 從 C# 6 開始,您可以使用下列語法來初始化字典及其他關聯容器。Beginning with C# 6, you can initialize dictionaries and other associative containers using the following syntax. 請注意,它會使用具有多個值的物件,而不是使用括弧和指派的索引子語法:Notice that instead of indexer syntax, with parentheses and an assignment, it uses an object with multiple values:

var moreNumbers = new Dictionary<int, string>
{
    {19, "nineteen" },
    {23, "twenty-three" },
    {42, "forty-two" }
};

初始設定式範例會呼叫 Add(TKey, TValue) 來將三個項目新增至字典。This initializer example calls Add(TKey, TValue) to add the three items into the dictionary. 由於編譯器所產生的方法呼叫,這兩種初始化關聯集合的方式會有稍微不同的行為。These two different ways to initialize associative collections have slightly different behavior because of the method calls the compiler generates. 這兩種方式都可以搭配 Dictionary 類別運作。Both variants work with the Dictionary class. 其他類型視其公用 API 而定,可能只支援其中一種。Other types may only support one or the other based on their public API.

範例Examples

下列範例結合了物件和集合初始設定式的概念。The following example combines the concepts of object and collection initializers.

public class InitializationSample
{
    public class Cat
    {
        // Auto-implemented properties.
        public int Age { get; set; }
        public string Name { get; set; }

        public Cat() { }

        public Cat(string name)
        {
            Name = name;
        }
    }

    public static void Main()
    {
        Cat cat = new Cat { Age = 10, Name = "Fluffy" };
        Cat sameCat = new Cat("Fluffy"){ Age = 10 };

        List<Cat> cats = new List<Cat>
        {
            new Cat { Name = "Sylvester", Age = 8 },
            new Cat { Name = "Whiskers", Age = 2 },
            new Cat { Name = "Sasha", Age = 14 }
        };

        List<Cat> moreCats = new List<Cat>
        {
            new Cat { Name = "Furrytail", Age = 5 },
            new Cat { Name = "Peaches", Age = 4 },
            null
        };

        // Display results.
        System.Console.WriteLine(cat.Name);

        foreach (Cat c in cats)
            System.Console.WriteLine(c.Name);

        foreach (Cat c in moreCats)
            if (c != null)
                System.Console.WriteLine(c.Name);
            else
                System.Console.WriteLine("List element has null value.");
    }
    // Output:
    //Fluffy
    //Sylvester
    //Whiskers
    //Sasha
    //Furrytail
    //Peaches
    //List element has null value.
}

下列範例顯示實作 IEnumerable 並包含具有多個參數之 Add 方法的物件,它使用集合初始設定式來處理對應至 Add 方法簽章之清單中每個項目的多個元素。The following example shows an object that implements IEnumerable and contains an Add method with multiple parameters, It uses a collection initializer with multiple elements per item in the list that correspond to the signature of the Add method.

    public class FullExample
    { 
        class FormattedAddresses : IEnumerable<string>
        {
            private List<string> internalList = new List<string>();
            public IEnumerator<string> GetEnumerator() => internalList.GetEnumerator();

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => internalList.GetEnumerator();

            public void Add(string firstname, string lastname, 
                string street, string city, 
                string state, string zipcode) => internalList.Add(
                $@"{firstname} {lastname}
{street}
{city}, {state} {zipcode}"
                );
        }

        public static void Main()
        {
            FormattedAddresses addresses = new FormattedAddresses()
            {
                {"John", "Doe", "123 Street", "Topeka", "KS", "00000" },
                {"Jane", "Smith", "456 Street", "Topeka", "KS", "00000" }
            };

            Console.WriteLine("Address Entries:");

            foreach (string addressEntry in addresses)
            {
                Console.WriteLine("\r\n" + addressEntry);
            }
        }

        /*
         * Prints:
         
            Address Entries:

            John Doe
            123 Street
            Topeka, KS 00000

            Jane Smith
            456 Street
            Topeka, KS 00000
         */
    }

Add 方法可以使用 params 關鍵字來接受各種數目的引數,如下列範例所示。Add methods can use the params keyword to take a variable number of arguments, as shown in the following example. 此範例也示範索引子的自訂實作,以使用索引來初始化集合。This example also demonstrates the custom implementation of an indexer to initialize a collection using indexes.

public class DictionaryExample
{ 
    class RudimentaryMultiValuedDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, List<TValue>>>
    {
        private Dictionary<TKey, List<TValue>> internalDictionary = new Dictionary<TKey, List<TValue>>();

        public IEnumerator<KeyValuePair<TKey, List<TValue>>> GetEnumerator() => internalDictionary.GetEnumerator();

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => internalDictionary.GetEnumerator();

        public List<TValue> this[TKey key]
        {
            get => internalDictionary[key];
            set => Add(key, value);
        }

        public void Add(TKey key, params TValue[] values) => Add(key, (IEnumerable<TValue>)values);

        public void Add(TKey key, IEnumerable<TValue> values)
        {
            if (!internalDictionary.TryGetValue(key, out List<TValue> storedValues))
                internalDictionary.Add(key, storedValues = new List<TValue>());

            storedValues.AddRange(values);
        }
    }

    public static void Main()
    {
        RudimentaryMultiValuedDictionary<string, string> rudimentaryMultiValuedDictionary1
            = new RudimentaryMultiValuedDictionary<string, string>()
            {
                {"Group1", "Bob", "John", "Mary" },
                {"Group2", "Eric", "Emily", "Debbie", "Jesse" }
            };
        RudimentaryMultiValuedDictionary<string, string> rudimentaryMultiValuedDictionary2
            = new RudimentaryMultiValuedDictionary<string, string>()
            {
                ["Group1"] = new List<string>() { "Bob", "John", "Mary" },
                ["Group2"] = new List<string>() { "Eric", "Emily", "Debbie", "Jesse" }
            };
        RudimentaryMultiValuedDictionary<string, string> rudimentaryMultiValuedDictionary3
            = new RudimentaryMultiValuedDictionary<string, string>()
            {
                {"Group1", new string []{ "Bob", "John", "Mary" } },
                { "Group2", new string[]{ "Eric", "Emily", "Debbie", "Jesse" } }
            };

        Console.WriteLine("Using first multi-valued dictionary created with a collection initializer:");

        foreach (KeyValuePair<string, List<string>> group in rudimentaryMultiValuedDictionary1)
        {
            Console.WriteLine($"\r\nMembers of group {group.Key}: ");

            foreach (string member in group.Value)
            {
                Console.WriteLine(member);
            }
        }

        Console.WriteLine("\r\nUsing second multi-valued dictionary created with a collection initializer using indexing:");

        foreach (KeyValuePair<string, List<string>> group in rudimentaryMultiValuedDictionary2)
        {
            Console.WriteLine($"\r\nMembers of group {group.Key}: ");

            foreach (string member in group.Value)
            {
                Console.WriteLine(member);
            }
        }
        Console.WriteLine("\r\nUsing third multi-valued dictionary created with a collection initializer using indexing:");

        foreach (KeyValuePair<string, List<string>> group in rudimentaryMultiValuedDictionary3)
        {
            Console.WriteLine($"\r\nMembers of group {group.Key}: ");

            foreach (string member in group.Value)
            {
                Console.WriteLine(member);
            }
        }
    }

    /*
     * Prints:
     
        Using first multi-valued dictionary created with a collection initializer:

        Members of group Group1:
        Bob
        John
        Mary

        Members of group Group2:
        Eric
        Emily
        Debbie
        Jesse

        Using second multi-valued dictionary created with a collection initializer using indexing:

        Members of group Group1:
        Bob
        John
        Mary

        Members of group Group2:
        Eric
        Emily
        Debbie
        Jesse

        Using third multi-valued dictionary created with a collection initializer using indexing:

        Members of group Group1:
        Bob
        John
        Mary

        Members of group Group2:
        Eric
        Emily
        Debbie
        Jesse
     */
}

另請參閱See also