C# 允许您在单个语句中实例化对象或集合并执行成员分配。
对象初始化器
对象初始化器允许您在创建时为对象的任何可访问字段或属性赋值,而无需调用构造函数,然后调用赋值语句行。 对象初始值设定项语法使您能够为构造函数指定参数或省略参数(和括号语法)。 以下示例显示如何使用具有命名类型 Cat 的对象初始值设定项以及如何调用无参数构造函数。 请注意在 Cat 类中使用自动实现的属性。
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 };
从 C# 6 开始,对象初始化器除了分配字段和属性外,还可以设置索引器。 考虑这个基本的 Matrix 类:
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; }
}
}
您可以使用以下代码初始化单位矩阵:
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 { ... }; }
具有匿名类型的对象初始化器
查询表达式经常使用匿名类型,只能使用对象初始化器来初始化,如下面的声明所示。
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" }
};
此初始化器示例调用 Add(TKey, TValue) 将三个项目添加到字典中。 由于编译器生成的方法调用,这两种初始化关联集合的不同方法的行为略有不同。 这两种变体都适用于 Dictionary 类。 其他类型可能仅支持基于其公共 API 的一种或另一种。
具有集合只读属性初始化的对象初始化器
某些类可能具有只读属性的集合属性,例如 CatOwner 在以下情况下的 Cats 属性:
public class CatOwner
{
public IList<Cat> Cats { get; } = new List<Cat>();
}
您将无法使用到目前为止讨论的集合初始化器语法,因为无法为该属性分配新列表:
CatOwner owner = new CatOwner
{
Cats = new List<Cat>
{
new Cat{ Name = "Sylvester", Age=8 },
new Cat{ Name = "Whiskers", Age=2 },
new Cat{ Name = "Sasha", Age=14 }
}
};
然而,通过省略列表创建(new List<Cat>),仍然可以使用初始化语法将新条目添加到 Cats,如下所示:
CatOwner owner = new CatOwner
{
Cats =
{
new Cat{ Name = "Sylvester", Age=8 },
new Cat{ Name = "Whiskers", Age=2 },
new Cat{ Name = "Sasha", Age=14 }
}
};
要添加的条目集只是用大括号括起来。 下面代码与以上写法相同:
CatOwner owner = new CatOwner();
owner.Cats.Add(new Cat{ Name = "Sylvester", Age=8 });
owner.Cats.Add(new Cat{ Name = "Whiskers", Age=2 });
owner.Cats.Add(new Cat{ Name = "Sasha", Age=14 });
例子
以下示例结合了对象和集合初始值设定项的概念。
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 方法的签名。
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
*/
}
添加方法可以使用 params 关键字来获取可变数量的参数,如下例所示。 此示例还演示了使用索引初始化集合的索引器的自定义实现。(此示例还是以后在研究吧)
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
*/
}