【问题标题】:Real world use cases for C# indexers?C# 索引器的真实用例?
【发布时间】:2011-01-12 04:57:50
【问题描述】:

我已经看过很多关于 c# 索引器的示例,但它在现实生活中对我有什么帮助。

我知道如果它不是一个严肃的特性,C# 大师不会添加这个,但我想不出现实世界的情况(不是 foo bar 的东西)来使用索引器。

注意:我知道related question 存在,但它对我没有多大帮助。

【问题讨论】:

  • 不是在List-type、LinkedList-type、Dictionary-type或其他集合中? ;) 通常当您有一个列表时,提供索引器被认为是有用的,因此可以使用 myList[k] 而不是 myList.Get(k) 或 VB 版本的 myList.Item(k)
  • @Skurmedel 请解释一下,我显然遗漏了一些东西
  • 所以我有一个包含字段“col”的类,它是一个集合,当我需要访问“col”中的特定值时,我将使用索引器跨度>
  • 是的,如果你的类模仿某种存储或者它包含你可能想要检索的单个数据片段。 String 有一个索引器,因此您可以执行 str[1] 来检索第二个字符。 FontStorage[key] 可能有意义,Font[key] 可能没有。数据如何存储通常并不重要,但接口(外观/表示)决定了索引器是否有意义。也就是说,我很少使用它们,但有时它们会派上用场。我制作了一个 ASP.NET 缓存包装器,其中 Cache["key"] 有意义,是一个真实世界的场景;)
  • 不要忘记 DataSet 和 DataTable 以及 DataColumn 和 DataRow。这些也都使用索引器!

标签: c# indexer


【解决方案1】:

我看待索引器的方式是(正确或错误!),通过索引访问某些内容应该比以任何其他方式访问它更有效,因为在某种程度上,形状或形式,我正在使用其索引器的类存储某种形式的 index,以便在以这种方式访问​​时快速查找值。

经典示例是一个数组,当您使用代码 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];
    }
}

只是我的 2p,但是,就像我说的,.. 我一直认为“索引器”是从某物中获取数据的一种权宜之计。

【讨论】:

    【解决方案2】:

    Skurmedel 提到的最明显的例子是List&lt;T&gt;Dictionary&lt;TKey, TValue&gt;。你更喜欢什么:

    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
    

    ?现在,您可能需要编写一个索引器(通常在您创建一个类似集合的类时)相对较少,但我怀疑您使用它们相当频繁。

    【讨论】:

    • 这是否意味着不能存在多个索引器。或者换句话说,它们可以超载吗?
    • @Vivek Bernard:它们可能会超载。
    【解决方案3】:

    Microsoft has an example 使用索引器将文件视为字节数组。

    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 样式数组,允许您使用字符串和数字作为同一类中的索引(通过重载索引器)。归根结底,它当然是语法糖,但它有助于简化思维并使它们更具可读性(并且使用起来更舒适)。
    【解决方案4】:

    假设您有一个对象集合,您希望能够按其在集合中的放置顺序以外的方式对其进行索引。在下面的示例中,您可以看到如何使用某个对象的“位置”属性并使用索引器,返回集合中与您的位置匹配的所有对象,或者在第二个示例中,所有包含某个 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 有了泛型,我实现索引器(实现强类型集合)的最大原因就消失了。

      【讨论】:

      • 为什么使用泛型会减少对索引器的需求?还是您的意思是您不再需要编写自己的集合?
      【解决方案6】:

      它只是集合类型类的语法糖。我从来没有理由写这样一门课。所以我认为在“现实生活”中很少使用它,因为使用它的类已经实现了。

      【讨论】:

      • 不要忘记一个人的稀有性是另一个人的要求。虽然它们是语法糖......你可以说大部分框架。 (枚举、属性、运算符重载、Linq、WCF 等)
      • @Matthew:拥有索引器或拥有GetElement 方法有什么区别吗?索引器使用起来更短。
      • 使用 Property 和 GetProperty 方法也没有区别。如果您在编译的代码中,这会在后台发生。我们使用的每个关键字(除了 CPU 或运行时的操作码)都是语法糖的形式……而我,就其中之一,喜欢糖。例如...当您在 C# 中使用索引器时,您使用 this 关键字在类上声明它。在 VB.Net 上,您只需选择一个属性并添加一个输入参数。
      • @Matthew:我是说它没用还是我不喜欢它?我只是说没有什么特别之处,只是为了让集合类型类的使用更好。我不知道你想告诉我什么。
      • 我也不是故意的。您的回答就像我看到的许多关于“不需要”和“不需要”功能的回答。 (由于添加了 LINQ 语法,人们一直在抱怨。)如果您都支持索引器,那么我们同意 :) 我说在您需要或想要使用它们的地方使用它们。这些东西只是帮助我们作为开发人员的生活更轻松的工具。
      【解决方案7】:

      在 ASP.Net 中,有时会使用索引器,例如从任何 Request、Session 或 Application 对象中读取内容。我经常看到某些东西存储在 Session 或 Application 对象中只是为了反复使用。

      【讨论】:

        【解决方案8】:
        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();
                }
            }
        }
        

        【讨论】:

          【解决方案9】:

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

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

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

          因此,对象采用索引值来引用私有或公开存储在类中的 Integer 变量。假设您没有此功能,那么您可能会以这种方式引用(会更长):

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

          其中 RollNumberVariable 是整数变量,表示当前学生对象的卷号。

          更多详情http://code-kings.blogspot.in/2012/09/indexers-in-c-5.html

          【讨论】:

            【解决方案10】:

            这是我创建的视频http://www.youtube.com/watch?v=HdtEQqu0yOY,下面是关于相同内容的详细说明。

            索引器有助于使用简化的接口在类中访问包含的集合。它是一种语法糖。

            例如,假设您有一个客户类,其中包含地址集合。现在假设我们想通过“Pincode”和“PhoneNumber”获取地址集合。因此,合乎逻辑的步骤是创建两个重载函数,一个使用“PhoneNumber”获取,另一个使用“PinCode”获取。您可以在下面的代码中看到我们定义了两个函数。

            Customer Customers = new Customer();
            Customers.getAddress(1001);
            Customers.getAddress("9090");
            

            如果你使用索引器,你可以用下面的代码来简化上面的代码。

            Customer Customers = new Customer();
            Address o = Customers[10001];
            o = Customers["4320948"];
            

            干杯。

            【讨论】:

            • 恕我直言,这个例子不是最好的,原因如下:索引器必须具有数组的语义,所以 Customers[1001] 应该返回一个客户,而不是像地址这样的其他类型。在您的情况下,您最好使用 getAddress 来传达正确的意图。否则,我会将实现索引器的类命名为地址,然后获取地址作为地址 [1001]
            【解决方案11】:

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

            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(或任何其他并发集合)时尤其有用。

            【讨论】:

              【解决方案12】:

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

              使用系统;

              命名空间 Indexers_Example

              {

              class Indexers
              {
              
                  private Int16[] RollNumberVariable;
              
                  public Indexers(Int16 size)
                  {
                      RollNumberVariable = new Int16[size];
              
                      for (int i = 0; i < size; i++)
                      {
                          RollNumberVariable[i] = 0;
                      }
                  }
              
                  public Int16 this[int pos]
                  {
                      get
                      {
                          return RollNumberVariable[pos];
                      }
                      set
                      {
                          RollNumberVariable[pos] = value;
                      }
                  }
              }
              

              }

              【讨论】:

                【解决方案13】:

                添加到@code-kings 帖子。

                此外,调用RollNumberVariable[0] 将触发默认集合的索引器的行为。虽然 indexers 实际上是 properties,但您可以在提取数据时编写自己的逻辑。您可以轻松地将大部分索引参数值委托给内部集合,但您也可以为某些索引值返回任意值。

                只是一个示例 - 您可以拥有 2 个以上不同格式的内部集合,但外部用户将通过单个索引器与它们进行交互(这将作为一个调度程序工作), 而这些集合将被隐藏。这在很大程度上鼓励了封装原则。

                【讨论】:

                  【解决方案14】:

                  我正在尝试从序列文件中获取图像。我需要某种二维数组或锯齿状数组来保存像素值。我使用索引器而不是数组,因为在索引器上循环比在 2D 或锯齿状数组上循环更快。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2012-10-18
                    • 1970-01-01
                    相关资源
                    最近更新 更多