C# несколько индексаторов

Возможно ли иметь что-то вроде следующего:

class C
{
    public Foo Foos[int i]
    {
        ...
    }

    public Bar Bars[int i]
    {
        ...
    }
}

Если нет, то каким образом я могу достичь этого? Я знаю, что мог бы создавать функции с именами getFoo(int i) и getBar(int i), но я надеялся сделать это со свойствами.

8 ответов

Решение

Нет в C#, нет.

Однако вы всегда можете вернуть коллекции из свойств следующим образом:

public IList<Foo> Foos
{
    get { return ...; }
}

public IList<Bar> Bars
{
    get { return ...; }
}

IList имеет индексатор, поэтому вы можете написать следующее:

C whatever = new C();
Foo myFoo = whatever.Foos[13];

На строках "вернись...;" вы можете вернуть все, что реализует IList, но вы можете, что бы вернуть оболочку только для чтения вокруг вашей коллекции, см. метод AsReadOnly().

Это из C# 3.0 spec

"Перегрузка индексаторов позволяет классу, структуре или интерфейсу объявлять несколько индексаторов при условии, что их сигнатуры уникальны в этом классе, структуре или интерфейсе".

public class MultiIndexer : List<string>  
{
    public string this[int i]
    {
        get{
            return this[i];
        }
    }
    public string this[string pValue]
    {
        get
        {
            //Just to demonstrate
            return this.Find(x => x == pValue);  
        }
    }      
}

Есть способ... если вы определите 2 новых типа, чтобы компилятор мог различать две разные сигнатуры...

  public struct EmployeeId
  { 
      public int val;
      public EmployeeId(int employeeId) { val = employeeId; }
  }
  public struct HRId
  { 
      public int val;
      public HRId(int hrId) { val = hrId; }
  }
  public class Employee 
  {
      public int EmployeeId;
      public int HrId;
      // other stuff
  }
  public class Employees: Collection<Employee>
  {
      public Employee this[EmployeeId employeeId]
      {
          get
             {
                foreach (Employee emp in this)
                   if (emp.EmployeeId == employeeId.val)
                      return emp;
                return null;
             }
      }
      public Employee this[HRId hrId]
      {
          get
             {
                foreach (Employee emp in this)
                   if (emp.HRId == hrId.val)
                      return emp;
                return null;
             }
      }
      // (or using new C#6+ "expression-body" syntax)
      public Employee this[EmployeeId empId] => 
             this.FirstorDefault(e=>e.EmployeeId == empId .val;
      public Employee this[HRId hrId] => 
             this.FirstorDefault(e=>e.EmployeeId == hrId.val;

  }

Затем, чтобы позвонить, вы должны написать:

Employee Bob = MyEmployeeCollection[new EmployeeID(34)];

И если вы написали неявный оператор преобразования:

public static implicit operator EmployeeID(int x)
{ return new EmployeeID(x); }

тогда вам даже не нужно было бы делать это, чтобы использовать это, вы могли бы просто написать:

Employee Bob = MyEmployeeCollection[34];

То же самое применимо, даже если два индексатора возвращают разные типы...

Попробуйте мой класс IndexProperty, чтобы включить несколько индексаторов в одном классе

http://www.codeproject.com/Tips/319825/Multiple-Indexers-in-Csharp

Если вы пытаетесь сделать что-то вроде этого:

var myClass = new MyClass();

Console.WriteLine(myClass.Foos[0]);
Console.WriteLine(myClass.Bars[0]);

затем вам нужно определить индексаторы для самих классов Foo и Bar - т.е. поместить все объекты Foo в Foos и сделать Foos экземпляром типа, который напрямую поддерживает индексирование.

Чтобы продемонстрировать использование массивов для свойств элементов (поскольку они уже поддерживают индексаторы):

public class C {
    private string[] foos = new string[] { "foo1", "foo2", "foo3" };
    private string[] bars = new string[] { "bar1", "bar2", "bar3" };
    public string[] Foos { get { return foos; } }
    public string[] Bars { get { return bars; } }
}

позволил бы вам сказать:

 C myThing = new C();
 Console.WriteLine(myThing.Foos[1]);
 Console.WriteLine(myThing.Bars[2]);

Я считаю, что принятый ответ неверен. Это возможно, если вы будете использовать явную реализацию интерфейса:

      class C
{
    public IFooProvider Foos => this;
    public IBarProvider Bars => this;

    Foo IFooProvider.this[int i]
    {
        ...
    }

    Bar IBarProvider.this[int i]
    {
        ...
    }

    public interface IFooProvider
    {
        Foo this[int i] { get; set; }
    }

    public interface IBarProvider
    {
        Bar this[int i] { get; set; }
    }
}

Тогда вы можете использовать его так, как хотели:

      C c;
c.Foos[1] = new Foo();
c.Bars[0] = new Bar();

Нет, ты не можешь сделать это. Операторы преобразования являются только теми методами, у которых подписи могут отличаться только типом возвращаемого значения. Индексаторы должны иметь разные типы входных параметров, чтобы компилировать их.

C# не имеет перегрузки типа возврата. Вы можете определить несколько индексаторов, если их входные параметры отличаются.

Другие вопросы по тегам