C#索引器的实际用例是什么?

49

我已经看到了很多C#索引器的例子,但是它在现实生活中有什么用途呢?

我知道如果这不是一个重要的特性,C#大师就不会添加它,但是我想不出一个实际应用场景(不是那些虚构的东西)来使用索引器。

注意:我意识到相关问题存在,但它并没有对我有太大的帮助。


除了列表类型、链表类型、字典类型或其他集合外,还有哪些?;) 当你有一系列东西时,通常会考虑提供一个索引器,这样就可以使用myList[k]而不是myList.Get(k)或VB版本的myList.Item(k)。 - Skurmedel
@Skurmedel 请解释一下,我显然漏掉了什么。 - Vivek Bernard
所以我有一个包含字段“col”的类,它是一个集合,当我需要访问“col”中的特定值时,那就是我将使用索引器的时候。 - Vivek Bernard
6
如果你的类模拟存储某种数据或包含需要检索的单独数据,那么使用索引器可能很有用。字符串具有索引器,所以可以使用str[1]来检索第二个字符。FontStorage[key]可能有意义,而Font[key]可能没有意义。通常存储数据的方式并不重要,但接口(外观/表示)决定了是否需要一个索引器。话虽如此,我很少使用它们,但有时它们很方便。我制作了一个ASP.NET Cache包装器,在那里Cache["key"]是有意义的,这是一个现实世界的场景 ;) - Skurmedel
1
不要忘记 DataSet 和 DataTable 和 DataColumn 和 DataRow。它们都使用索引器! - Muad'Dib
显示剩余2条评论
14个回答

34
我对索引器的看法是,通过索引访问某个东西应该比以其他方式访问更有效,因为使用索引器的类在某种程度上存储了一些形式的索引,可以在访问时快速查找值。
经典的例子是数组,当你使用代码 myarray [3] 访问数组的第 n 个元素时,编译器/解释器知道数组元素的大小(占用内存的大小),并将其视为从数组开头的偏移量处理。你还可以使用“for(int i = 0; i < myarray.length; i++) { if (i = 3) then { .. do stuff } }”(虽然你永远不会想要这样做!),这将效率更低。这也说明了数组是一个不好的例子。
假设你有一个存储 DVD 的集合类:
public class DVDCollection
{
    private Dictionary<string, DVD> store = null;
    private Dictionary<ProductId, string> dvdsByProductId = null;

    public DVDCollection()
    {
        // gets DVD data from somewhere and stores it *by* TITLE in "store"
        // stores a lookup set of DVD ProductId's and names in "dvdsByProductid"
        store = new Dictionary<string, DVD>();
        dvdsByProductId = new Dictionary<ProductId, string>();
    }

    // Get the DVD concerned, using an index, by product Id
    public DVD this[ProductId index]  
    {
       var title = dvdsByProductId[index];
       return store[title];
    }
}

仅提供我的个人意见,但是,就像我说的那样.. 我一直认为"索引器"是从某些东西中获取数据的一种便捷方式。


25

正如Skurmedel所提到的那样,最明显的例子是List<T>Dictionary<TKey, TValue>。你更喜欢哪个?

List<string> list = new List<string> { "a", "b", "c" };
string value = list[1]; // This is using an indexer

Dictionary<string, string> dictionary = new Dictionary<string, string>
{
    { "foo", "bar" },
    { "x", "y" }
};
string value = dictionary["x"]; // This is using an indexer

? 现在你可能相对较少需要编写索引器(通常是在创建类似集合的类时),但我认为你相当频繁地使用它们。


1
这是否意味着不能存在多个索引器?换句话说,它们可以被重载吗? - Vivek Bernard
1
@Vivek Bernard:它们可以被重载。 - OregonGhost

11

微软提供了一个示例,使用索引器将文件视为字节数组。

public byte this[long index]
{
    // Read one byte at offset index and return it.
    get 
    {
        byte[] buffer = new byte[1];
        stream.Seek(index, SeekOrigin.Begin);
        stream.Read(buffer, 0, 1);
        return buffer[0];
    }
    // Write one byte at offset index and return it.
    set 
    {
        byte[] buffer = new byte[1] {value};
        stream.Seek(index, SeekOrigin.Begin);
        stream.Write(buffer, 0, 1);
    }
}

我认为这是一个很好的例子,因为索引器允许检索或存储项目,从而模拟数组行为。在这种情况下,它用于持久化(序列化和反序列化)数据,隐藏了访问文件的复杂性。它也可以是数据库。我还看到过.NET JSON序列化程序使用它来模拟JavaScript样式数组,允许您在同一类中使用字符串和数字作为索引(通过重载索引器)。最终,它当然是语法糖,但它有助于简化事物并使它们更易读(以及更舒适地使用)。 - Matt

7

假设您有一组对象,想要按照某些不同于它们在集合中放置顺序的方式进行索引。在下面的示例中,您可以看到如何使用某个对象的“位置”属性,并使用索引器返回与您位置匹配的集合中的所有对象,或者在第二个示例中,返回包含特定Count()对象的所有对象。

class MyCollection {

  public IEnumerable<MyObject> this[string indexer] {
    get{ return this.Where(p => p.Location == indexer); }
  }

  public IEnumerable<MyObject> this[int size] {
    get{ return this.Where(p => p.Count() == size);}
  }
}

5

一旦.NET拥有泛型,我实现索引器(用于实现强类型集合)的最大原因就消失了。


12
泛型的出现会减少索引器的需求吗?还是说你不再需要编写自己的集合类? - Skurmedel

3

这只是一种针对集合类型类的语法糖。我从来没有写过这样的类,因此我认为在“现实生活”中很少使用它,因为使用它的类已经被实现了。


不要忘记,一个人的稀有性是另一个人的需求。虽然它们是语法糖...你可以这样说大多数框架都是如此。(Enum,Properties,Operator Overloading,Linq,WCF等) - Matthew Whited
@Matthew:拥有索引器和拥有GetElement方法之间有什么区别吗?使用索引器更短。 - Stefan Steinegger
使用属性和GetProperty方法没有区别。如果你查看编译后的代码,这就在后台发生了。我们使用的每个关键字(超出CPU或运行时的OpCodes)都是一种语法糖...就我个人而言,我喜欢糖。例如...当你在C#中使用索引器时,你需要在类上声明它,并使用this关键字。在VB.Net中,你只需选择一个属性并添加一个输入参数即可。 - Matthew Whited
@Matthew:我说过它没用或者我不喜欢它吗?我只是说它没有什么特别的,只是让集合类型类的使用更加方便。我不知道你想告诉我什么。 - Stefan Steinegger
我并不是想要表达任何意思。你的答案只是看起来像我所见过的关于“不需要”和“不想要”的功能之一。(就像那些抱怨加入LINQ语法的人一样。)如果你支持索引器,那我们就同意了 :) 我认为只要你需要或者想要使用它们,就随时可以使用它们。这些都只是工具,帮助我们作为开发人员更加容易地生活。 - Matthew Whited

3
在ASP.Net中,有几个不同的时机使用索引器,例如从Request、Session或Application对象中读取某些内容。我经常看到将某些内容存储在Session或Application对象中,以便再次使用。

2

http://code-kings.blogspot.in/2012/09/indexers-in-c-5.html

索引器是C#程序中的元素,允许类表现为数组。您可以使用整个类作为数组。在此数组中,您可以存储任何类型的变量。变量存储在单独的位置上,但由类名本身寻址。为整数、字符串、布尔等创建索引器是可行的想法。这些索引器将有效地作用于类的对象。

假设您已经创建了一个类索引器,用于存储班级中学生的学号。进一步假设您已经创建了一个名为obj1的此类对象。当您说obj1 [0]时,您正在引用排名第一的学生。同样,obj1 [1]指的是排名第二的学生。

因此,该对象采用索引值来引用类中私有或公共存储的整数变量。假设您没有此功能,则可能会以更长的方式引用:

obj1.RollNumberVariable[0]
obj1.RollNumberVariable[1]. 

RollNumberVariable是指当前学生对象的学号整数变量。

更多细节请参见http://code-kings.blogspot.in/2012/09/indexers-in-c-5.html


2
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace IndexerSample
{
    class FailSoftArray 
    {
        int[] a; // reference to underlying array
        public int Length; // Length is public
        public bool ErrFlag; // indicates outcome of last operation
        // Construct array given its size.
        public FailSoftArray(int size)
        {
            a = new int[size];
            Length = size;
        }
        // This is the indexer for FailSoftArray.
        public int this[int index] 
        {
        // This is the get accessor.
            get
            {
                if (ok(index))
                {
                    ErrFlag = false;
                    return a[index];
                }
                else
                {
                    ErrFlag = true;
                    return 0;
                }
            }
            // This is the set accessor.
            set
            {
                if (ok(index))
                {
                    a[index] = value;
                    ErrFlag = false;
                }
                else ErrFlag = true;
            }
        }
        // Return true if index is within bounds.
        private bool ok(int index)
        {
            if (index >= 0 & index < Length) return true;
            return false;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            FailSoftArray fs = new FailSoftArray(5);
            int x;
            // Show quiet failures.
            Console.WriteLine("Fail quietly.");
            for (int i = 0; i < (fs.Length * 2); i++)
                fs[i] = i * 10;
            for (int i = 0; i < (fs.Length * 2); i++)
            {
                x = fs[i];
                if (x != -1) Console.Write(x + " ");
            }
            Console.WriteLine();
            // Now, display failures.
            Console.WriteLine("\nFail with error reports.");
            for (int i = 0; i < (fs.Length * 2); i++)
            {
                fs[i] = i * 10;
                if (fs.ErrFlag)
                    Console.WriteLine("fs[" + i + "] out-of-bounds");
            }
            for (int i = 0; i < (fs.Length * 2); i++)
            {
                x = fs[i];
                if (!fs.ErrFlag) Console.Write(x + " ");
                else
                    Console.WriteLine("fs[" + i + "] out-of-bounds");
            }
            Console.ReadLine();
        }
    }
}

1

您可以使用索引器来优雅地为非线程安全的字典(或任何非线程安全的集合)提供读/写多线程同步:

internal class ThreadSafeIndexerClass
{
    public object this[int key]
    {
        get
        {
            // Aquire returns IDisposable and does Enter() Exit() on a certain ReaderWriterLockSlim instance
            using (_readLock.Aquire()) 
            {
                object subset;
                _dictionary.TryGetValue(key, out foundValue);
                return foundValue;
            }
        }
        set
        {
            // Aquire returns IDisposable and does Enter() Exit() on a certain ReaderWriterLockSlim instance
            using (_writeLock.Aquire())
                _dictionary[key] = value;
        }
    }
}

当您不想使用笨重的ConcurrentDictionary(或任何其他并发集合)时,特别有用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接