索引器的详细讲解(C#)

索引器与属性很类似。索引器的创建与属性创建所使用的编程语言特性是一样的。索引器使属性被索引:使用一个或多个参数引用的属性,这些参数为某些值集合提供索引。

索引器语法
可以通过变量名和方括号访问索引器。将索引器参数放入方括号内:

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访问器)。
属性的各种用法同样适用于索引器(唯一例外的是“自动实现属性”,编译器无法始终为索引器生成正确的存储)。

创建索引器的必备要素:

  1. 必须先创建索引器所需要的容器(可以理解为数据的存放);
  2. 创建索引器需要使用this关键字;
  3. 索引器中必须包含get和set访问器,在C# 7.0后可以使用表达式(=>)主体进行简化;
  4. 在使用表达式主体成员进行索引时,必须额外的提供容器的修改接口(因为表达式主体不包括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访问器中数据操作的自定义处理,可以在访问器中对数据进行过滤或修正。

索引器总结:

  1. 使用索引器可以类似于数组的方式为对象建立索引;
  2. get取函数返回值,set取函数分配值;
  3. this(代表当前类)关键字用于定义索引器;
  4. value关键字用于定义set索引器所赋予的值;
  5. 索引器不必根据整数值进行索引,自行决定如何定义特定的查找机制;
  6. 索引器可以被重载;
  7. 索引器可以有多个形参,例如对二维数组的访问。

你可能感兴趣的:(C#,c#,开发语言,后端)