C#3.0新增功能06 对象和集合初始值设定项

  • 2019 年 10 月 4 日
  • 笔记

使用 C# 可以在单条语句中实例化对象或集合并执行成员分配。

对象初始值设定项

使用对象初始值设定项,你可以在创建对象时向对象的任何可访问字段或属性分配值,而无需调用后跟赋值语句行的构造函数。 利用对象初始值设定项语法,你可为构造函数指定参数或忽略参数(以及括号语法)。 以下示例演示如何使用具有命名类型 Cat 的对象初始值设定项以及如何调用无参数构造函数。 请注意,自动实现的属性在 Cat 类中的用法。

public class Cat  {      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 };

对象初始值设定项语法允许你创建一个实例,然后将具有其分配属性的新建对象指定给赋值中的变量。

从 C# 6 开始,除了分配字段和属性外,对象初始值设定项还可以设置索引器。 请思考这个基本的 Matrix 类:

public class Matrix  {      private double[,] storage = new double[3, 3];        public double this[int row, int column]      {          // 嵌入的数组将酌情抛出超出范围的异常。          get { return storage[row, column]; }          set { storage[row, column] = value; }      }  }

可以使用以下代码初始化标识矩阵:

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

包含可访问资源库的任何可访问索引器都可以用作对象初始值设定项中的表达式之一,这与参数的数量或类型无关。 索引参数构成左侧赋值,而表达式右侧是值。 例如,如果 IndexersExample 具有适当的索引器,则这些都是有效的:

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

对于要进行编译的前面的代码,IndexersExample 类型必须具有以下成员:

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

具有匿名类型的对象初始值设定项

尽管对象初始值设定项可用于任何上下文中,但它们在 LINQ 查询表达式中特别有用。 查询表达式常使用只能通过使用对象初始值设定项进行初始化的匿名类型,如下面的声明所示。

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

利用匿名类型,LINQ 查询表达式中的 select 子句可以将原始序列的对象转换为其值和形状可能不同于原始序列的对象。 如果你只想存储某个序列中每个对象的部分信息,则这很有用。 在下面的示例中,假定产品对象 (p) 包含很多字段和方法,而你只想创建包含产品名和单价的对象序列。

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

执行此查询时,productInfos 变量将包含一系列对象,这些对象可以在 foreach 语句中进行访问,如下面的示例所示:

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

新的匿名类型中的每个对象都具有两个公共属性,这两个属性接收与原始对象中的属性或字段相同的名称。 你还可在创建匿名类型时重命名字段;下面的示例将 UnitPrice 字段重命名为 Price

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

集合初始值设定项

在初始化实现 IEnumerable 的集合类型和初始化使用适当的签名作为实例方法或扩展方法的 Add 时,集合初始值设定项允许指定一个或多个元素初始值设定项。 元素初始值设定项可以是简单的值、表达式或对象初始值设定项。 通过使用集合初始值设定项,无需指定多个调用;编译器将自动添加这些调用。

下面的示例演示了两个简单的集合初始值设定项:

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 类的对象。 请注意,各个对象初始值设定项分别括在大括号中且用逗号隔开。

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 指定为集合初始值设定项中的一个元素。

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

如果集合支持读取/写入索引,可以指定索引元素。

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

前面的示例生成调用 Item[TKey] 以设置值的代码。 从 C# 6 开始,可以使用以下语法初始化字典和其他关联容器。 请注意,它使用具有多个值的对象,而不是带括号和赋值的索引器语法:

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

示例

下例结合了对象和集合初始值设定项的概念。

 1 public class InitializationSample   2 {   3     public class Cat   4     {   5         // 自动属性   6         public int Age { get; set; }   7         public string Name { get; set; }   8   9         public Cat() { }  10  11         public Cat(string name)  12         {  13             Name = name;  14         }  15     }  16  17     public static void Main()  18     {  19         Cat cat = new Cat { Age = 10, Name = "Fluffy" };  20         Cat sameCat = new Cat("Fluffy"){ Age = 10 };  21  22         List<Cat> cats = new List<Cat>  23         {  24             new Cat { Name = "Sylvester", Age = 8 },  25             new Cat { Name = "Whiskers", Age = 2 },  26             new Cat { Name = "Sasha", Age = 14 }  27         };  28  29         List<Cat> moreCats = new List<Cat>  30         {  31             new Cat { Name = "Furrytail", Age = 5 },  32             new Cat { Name = "Peaches", Age = 4 },  33             null  34         };  35  36         // 打印结果  37         System.Console.WriteLine(cat.Name);  38  39         foreach (Cat c in cats)  40             System.Console.WriteLine(c.Name);  41  42         foreach (Cat c in moreCats)  43             if (c != null)  44                 System.Console.WriteLine(c.Name);  45             else  46                 System.Console.WriteLine("List element has null value.");  47     }  48     // 输出:  49     //Fluffy  50     //Sylvester  51     //Whiskers  52     //Sasha  53     //Furrytail  54     //Peaches  55     //List element has null value.  56 }

下面的示例展示了实现 IEnumerable 且包含具有多个参数的 Add 方法的一个对象,它使用在列表中每项具有多个元素的集合初始值设定项,这些元素对应于 Add 方法的签名。

 1 public class FullExample   2     {   3         class FormattedAddresses : IEnumerable<string>   4         {   5             private List<string> internalList = new List<string>();   6             public IEnumerator<string> GetEnumerator() => internalList.GetEnumerator();   7   8             System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => internalList.GetEnumerator();   9  10             public void Add(string firstname, string lastname,  11                 string street, string city,  12                 string state, string zipcode) => internalList.Add(  13                 $@"{firstname} {lastname}  14                    {street}  15                    {city}, {state} {zipcode}"  16                 );  17         }  18  19         public static void Main()  20         {  21             FormattedAddresses addresses = new FormattedAddresses()  22             {  23                 {"John", "Doe", "123 Street", "Topeka", "KS", "00000" },  24                 {"Jane", "Smith", "456 Street", "Topeka", "KS", "00000" }  25             };  26  27             Console.WriteLine("Address Entries:");  28  29             foreach (string addressEntry in addresses)  30             {  31                 Console.WriteLine("rn" + addressEntry);  32             }  33         }  34  35         /*  36          * 输出:  37  38             Address Entries:  39  40             John Doe  41             123 Street  42             Topeka, KS 00000  43  44             Jane Smith  45             456 Street  46             Topeka, KS 00000  47          */  48     }

Add 方法可使用 params 关键字来获取可变数量的自变量,如下例中所示。 此示例还演示了索引器的自定义实现,以使用索引初始化集合。

  1 public class DictionaryExample    2 {    3     class RudimentaryMultiValuedDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, List<TValue>>>    4     {    5         private Dictionary<TKey, List<TValue>> internalDictionary = new Dictionary<TKey, List<TValue>>();    6    7         public IEnumerator<KeyValuePair<TKey, List<TValue>>> GetEnumerator() => internalDictionary.GetEnumerator();    8    9         System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => internalDictionary.GetEnumerator();   10   11         public List<TValue> this[TKey key]   12         {   13             get => internalDictionary[key];   14             set => Add(key, value);   15         }   16   17         public void Add(TKey key, params TValue[] values) => Add(key, (IEnumerable<TValue>)values);   18   19         public void Add(TKey key, IEnumerable<TValue> values)   20         {   21             if (!internalDictionary.TryGetValue(key, out List<TValue> storedValues))   22                 internalDictionary.Add(key, storedValues = new List<TValue>());   23   24             storedValues.AddRange(values);   25         }   26     }   27   28     public static void Main()   29     {   30         RudimentaryMultiValuedDictionary<string, string> rudimentaryMultiValuedDictionary1   31             = new RudimentaryMultiValuedDictionary<string, string>()   32             {   33                 {"Group1", "Bob", "John", "Mary" },   34                 {"Group2", "Eric", "Emily", "Debbie", "Jesse" }   35             };   36         RudimentaryMultiValuedDictionary<string, string> rudimentaryMultiValuedDictionary2   37             = new RudimentaryMultiValuedDictionary<string, string>()   38             {   39                 ["Group1"] = new List<string>() { "Bob", "John", "Mary" },   40                 ["Group2"] = new List<string>() { "Eric", "Emily", "Debbie", "Jesse" }   41             };   42         RudimentaryMultiValuedDictionary<string, string> rudimentaryMultiValuedDictionary3   43             = new RudimentaryMultiValuedDictionary<string, string>()   44             {   45                 {"Group1", new string []{ "Bob", "John", "Mary" } },   46                 { "Group2", new string[]{ "Eric", "Emily", "Debbie", "Jesse" } }   47             };   48   49         Console.WriteLine("Using first multi-valued dictionary created with a collection initializer:");   50   51         foreach (KeyValuePair<string, List<string>> group in rudimentaryMultiValuedDictionary1)   52         {   53             Console.WriteLine($"rnMembers of group {group.Key}: ");   54   55             foreach (string member in group.Value)   56             {   57                 Console.WriteLine(member);   58             }   59         }   60   61         Console.WriteLine("rnUsing second multi-valued dictionary created with a collection initializer using indexing:");   62   63         foreach (KeyValuePair<string, List<string>> group in rudimentaryMultiValuedDictionary2)   64         {   65             Console.WriteLine($"rnMembers of group {group.Key}: ");   66   67             foreach (string member in group.Value)   68             {   69                 Console.WriteLine(member);   70             }   71         }   72         Console.WriteLine("rnUsing third multi-valued dictionary created with a collection initializer using indexing:");   73   74         foreach (KeyValuePair<string, List<string>> group in rudimentaryMultiValuedDictionary3)   75         {   76             Console.WriteLine($"rnMembers of group {group.Key}: ");   77   78             foreach (string member in group.Value)   79             {   80                 Console.WriteLine(member);   81             }   82         }   83     }   84   85     /*   86      * 输出:   87   88         Using first multi-valued dictionary created with a collection initializer:   89   90         Members of group Group1:   91         Bob   92         John   93         Mary   94   95         Members of group Group2:   96         Eric   97         Emily   98         Debbie   99         Jesse  100  101         Using second multi-valued dictionary created with a collection initializer using indexing:  102  103         Members of group Group1:  104         Bob  105         John  106         Mary  107  108         Members of group Group2:  109         Eric  110         Emily  111         Debbie  112         Jesse  113  114         Using third multi-valued dictionary created with a collection initializer using indexing:  115  116         Members of group Group1:  117         Bob  118         John  119         Mary  120  121         Members of group Group2:  122         Eric  123         Emily  124         Debbie  125         Jesse  126      */  127 }