索引器与属性很类似。索引器的创建与属性创建所使用的编程语言特性是一样的。索引器使属性被索引:使用一个或多个参数引用的属性,这些参数为某些值集合提供索引。
索引器语法
可以通过变量名和方括号访问索引器。将索引器参数放入方括号内:
var item = SomeObject["key"];
SomeObject["keyOther"] = item;
使用this关键字作为属性名声明索引器,并在方括号内声明参数:
public int this[string Key]
{
get => storage.Find[Key];
set { storage.Set(Key, value); }
}
索引器可以使用任何有效的访问修饰符(public、protected internal、protected、internal、private、private protected)。它们可能是密封、虚拟或者抽象的。与属性一样,可以在索引器中为get和set访问器指定不同的访问修饰符。还可以指定只读索引器(忽略set访问器)或只写索引器(忽略get访问器)。
属性的各种用法同样适用于索引器(唯一例外的是“自动实现属性”,编译器无法始终为索引器生成正确的存储)。
创建索引器的必备要素:
- 必须先创建索引器所需要的容器(可以理解为数据的存放);
- 创建索引器需要使用this关键字;
- 索引器中必须包含get和set访问器,在C# 7.0后可以使用表达式(=>)主体进行简化;
- 在使用表达式主体成员进行索引时,必须额外的提供容器的修改接口(因为表达式主体不包括set访问器)。
索引器相当于一个方法,支持多个或多种类型的参数,与方法不同的是,索引器没有独立的名称,只能通过返回值的不同和参数的不同来区分不同的签名(从而实现重载),其返回值不能为void。索引器除了可以传入参数外,还可以对其进行赋值。
创建索引器时,其返回值为value关键字所使用的类型,定义了返回值类型的同时,也定义了其可接受的值类型。
总之,我对索引器的理解就是:是一个可以进行读写操作的自定义类中数据集合的接口。通过该接口,简化或丰富了该自定义类中数据集合的操作方式。
例如,在如下示例中,此索引器使用List作为容器,使用int类型的index进行索引,返回值为Measurements对象。
public class DataSamples
{
private class Page
{
private readonly List<Measurements> pageData = new List<Measurements>();
private readonly int startingIndex;
private readonly int length;
private bool dirty;
private DateTime lastAccess;
public Page(int startingIndex, int length)
{
this.startingIndex = startingIndex;
this.length = length;
lastAccess = DateTime.Now;
var generator = new Random();
for(int i = 0; i < length; i++)
{
var m = new Measurements
{
HiTemp = generator.Next(50, 95),
LoTemp = generator.Next(12, 49),
AirPressure = 28.0 + generator.NextDouble() * 4
};
pageData.Add(m);
}
}
public bool HasItem(int index) =>
((index >= startingIndex) && (index < startingIndex + length));
public Measurements this[int index]
{
get
{
lastAccess = DateTime.Now;
return pageData[index - startingIndex];
}
set
{
pageData[index - startingIndex] = value;
dirty = true;
lastAccess = DateTime.Now;
}
}
public bool Dirty => dirty;
public DateTime LastAcess => lastAccess;
}
private readonly int totalSize;
private readonly List<Page> pageInMemory = new List<Page>();
public DataSamples(int totalSize)
{
this.totalSize = totalSize;
}
public Measurements this[int index]
{
get
{
if (index < 0)
throw new IndexOutOfRangeException("index不能小于0!");
if (index >= totalSize)
throw new IndexOutOfRangeException("index大小超出存储空间!");
var page = updateCachedPagesForAccess(index);
return page[index];
}
set
{
if (index < 0)
throw new IndexOutOfRangeException("index不能小于0!");
if(index >= totalSize)
throw new IndexOutOfRangeException("index大小超出存储空间!");
var page = updateCachedPagesForAccess(index);
page[index] = value;
}
}
private Page updateCachedPagesForAccess(int index)
{
foreach(var p in pageInMemory)
{
if (p.HasItem(index))
return p;
}
var startingIndex = (index / 1000) * 1000;
var newPage = new Page(startingIndex, 1000);
addPageToCache(newPage);
return newPage;
}
private void addPageToCache(Page p)
{
if(pageInMemory.Count > 4)
{
var oldest = pageInMemory
.Where(page => !page.Dirty)
.OrderBy(page => page.LastAcess)
.FirstOrDefault();
if (oldest != null)
pageInMemory.Remove(oldest);
}
pageInMemory.Add(p);
}
}
上面的例子中,仅仅是对于索引器的认识,在实际工作中使用价值不大,因为所做的操作完全可以使用.NET中预定义的数据集合来完成。我个人觉得索引器的最大价值在于get和set访问器中数据操作的自定义处理,可以在访问器中对数据进行过滤或修正。
索引器总结:
- 使用索引器可以类似于数组的方式为对象建立索引;
- get取函数返回值,set取函数分配值;
- this(代表当前类)关键字用于定义索引器;
- value关键字用于定义set索引器所赋予的值;
- 索引器不必根据整数值进行索引,自行决定如何定义特定的查找机制;
- 索引器可以被重载;
- 索引器可以有多个形参,例如对二维数组的访问。