对象和集合初始值设定项(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 default 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,
};

包含可访问资源库的任何可访问索引器都可以用作对象初始值设定项中的表达式之一,这与参数的数量或类型无关。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',
    Baz = 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