8.6 实现自定义的索引运算符
自定义索引器不能使用运算符重载语法来实现,但是它们可以用与属性非常相似的语法来实现。
首先看看数组元素的访问。这里创建一个int元素数组。第二行代码使用索引器来访问第二个元素,并给它传递42。第三行使用索引器来访问第三个元素,并给该元素传递变量x。
int[] arr1 = {1, 2, 3}; arr1[1] = 42; int x = arr1[2];
注意:数组在第7章阐述。
CustomIndexerSample使用如下依赖项和名称空间:
依赖项:
NETStandard.Library
名称空间:
System System.Collections.Generic System.Linq static System.Console
要创建自定义索引器,首先要创建一个Person类,其中包含FirstName、LastName和Birthday只读属性(代码文件CustomIndexerSample/Person.cs):
public class Person { public DateTime Birthday { get; } public string FirstName { get; } public string LastName { get; } public Person(string firstName, string lastName, DateTime birthDay) { FirstName = firstName; LastName = lastName; Birthday = birthDay; } public override string ToString() => $"{FirstName} {LastName}"; }
类PersonCollection定义了一个包含Person元素的私有数组字段,以及一个可以传递许多Person对象的构造函数(代码文件CustomIndexerSample/PersonCollection.cs):
public class PersonCollection { private Person[] _people; public PersonCollection(params Person[] people) { _people = people.ToArray(); } }
为了允许使用索引器语法访问PersonCollection并返回Person对象,可以创建一个索引器。索引器看起来非常类似于属性,因为它也包含get和set访问器。两者的不同之处是名称。指定索引器要使用this关键字。this关键字后面的括号指定索引使用的类型。数组提供int类型的索引器,所以这里使用int类型直接把信息传递给被包含的数组people。get和set访问器的使用非常类似于属性。检索值时调用get访问器,在右边传递Person对象时调用set访问器。
public Person this[int index] { get { return _people[index]; } set { _people[index] = value; } }
对于索引器,不能仅定义int类型作为索引类型。任何类型都是有效的,如下面的代码所示,其中把DateTime结构作为索引类型。这个索引器用来返回有指定生日的每个人。因为多个人员可以有相同的生日,所以不是返回一个Person对象,而是用接口IEnumerable < Person>返回一个Person对象列表。所使用的Where方法根据lambda表达式进行过滤。Where方法在名称空间System.Linq中定义:
public IEnumerable<Person> this[DateTime birthDay] { get { return _people.Where(p => p.Birthday == birthDay); } }
使用DateTime类型的索引器检索人员对象,但不允许把人员对象设置为只有get访问器,而没有set访问器。在C# 6中有一个速记符号,可使用表达式主体的成员创建相同的代码(属性也可使用该语法):
public IEnumerable<Person> this[DateTime birthDay] => _people.Where(p => p.Birthday == birthDay);
示例应用程序的Main方法创建一个PersonCollection对象,给构造函数传递四个Person对象。在第一个WriteLine方法中,使用索引器的get访问器和int参数访问第三个元素。在foreach循环中,带有DateTime参数的索引器用来传递指定的日期(代码文件CustomIndexerSample/Program.cs):
static void Main() { var p1 = new Person("Ayrton", "Senna", new DateTime(1960, 3, 21)); var p2 = new Person("Ronnie", "Peterson", new DateTime(1944, 2, 14)); var p3 = new Person("Jochen", "Rindt", new DateTime(1942, 4, 18)); var p4 = new Person("Francois", "Cevert", new DateTime(1944, 2, 25)); var coll = new PersonCollection(p1, p2, p3, p4); WriteLine(coll[2]); foreach (var r in coll[new DateTime(1960, 3, 21)]) { WriteLine(r); } ReadLine(); }
运行程序,第一个WriteLine方法把Jochen Rindt写到控制台;foreach循环的结果是Ayrton Senna,因为他的生日是第二个索引器中指定的日期。