4

I have two arrays in my Base class, and I want to create Indexers that can be used in both of them, attached below is an MVCE of what I am trying to do.

class Indexer
      {
      private string[] namelist = new string[size];
      private char[] grades = new string[size];
      static public int size = 10;

      public IndexedNames() {
         for (int i = 0; i < size; i++){
            namelist[i] = "N. A.";
            grades[i] = 'F';
         }
      }
      public string this[int index] {
         get {
            string tmp;

            if( index >= 0 && index <= size-1 ) {
               tmp = namelist[index];
            } else {
               tmp = "";
            }

            return ( tmp );
         }
         set {
            if( index >= 0 && index <= size-1 ) {
               namelist[index] = value;
            }
         }
      }

In the above coed if you comment out the lines private char[] grades = new string[size]; and grades[i] = 'F'; then you can use the indexers as object_name[i] but I want to be able to access both namelist and grades by indexers.

Note : I cannot use structures to wrap them together as in my application, there size may not always be same.

Is this possible or I would need to go around with some hack.

Edit I am looking for something like names.namelist[i] and names.grades[i], or some statements that I can access them separately. Also Indexer logic is not consistent, and even size varies in some arrays, that was skipped here to aid simplicity in MVCE.

2
  • "but I want to be able to access both namelist and grades by indexers" separately ? Commented Mar 19, 2019 at 5:39
  • @MichaelRandall Yes separately like names.namelist[0] and names.grades[0] I mean whatever would be the suitable statement for this. Commented Mar 19, 2019 at 5:41

3 Answers 3

5

Sorry, no-can-do.

Although Indexers can be Overloaded and can have more than one formal parameter, you can't make two variations based on the same Parameter in the same class. This is a Language Limitation (or blessing).

Indexers (C# Programming Guide)

However, this should lead you to several options.

  1. You can just make use of C#7. Ref returns

Starting with C# 7.0, C# supports reference return values (ref returns). A reference return value allows a method to return a reference to a variable, rather than a value, back to a caller. The caller can then choose to treat the returned variable as if it were returned by value or by reference. The caller can create a new variable that is itself a reference to the returned value, called a ref local.

public ref string Namelist(int position)
{

     if (array == null)
         throw new ArgumentNullException(nameof(array));

     if (position < 0 || position >= array.Length)
         throw new ArgumentOutOfRangeException(nameof(position));

     return ref array[position];
}

...

// Which allows you to do funky things like this, etc.

object.NameList(1) = "bob";

  1. You could make sub/nested classes with indexers

That's to say, you could create a class that has the features you need with indexers, and make them properties of the main class. So you get something like you envisaged object.Namelist[0] and object.Grades[0].

Note : in this situation you could pass the arrays down as references and still access them in the main array like you do.


Example which includes both:

Given

public class GenericIndexer<T>
{
   private T[] _array;

   public GenericIndexer(T[] array)
   {
      _array = array;
   }
   public T this[int i]
   {
      get => _array[i];
      set => _array[i] = value;
   }
}

Class

public class Bobo
{
   private int[] _ints = { 2, 3, 4, 5, 5 };
   private string[] _strings = { "asd","asdd","sdf" };

   public Bobo()
   {

      Strings = new GenericIndexer<string>(_strings);
      Ints = new GenericIndexer<int>(_ints);
   }
   public GenericIndexer<string> Strings ;
   public GenericIndexer<int> Ints ;

   public void Test()
   {
      _ints[0] = 234;
   }

   public ref int DoInts(int pos)  => ref _ints[pos];
   public ref string DoStrings(int pos)  => ref _strings[pos];
}

Usage:

var bobo = new Bobo();
bobo.Ints[1] = 234;
bobo.DoInts(1) = 42;
Sign up to request clarification or add additional context in comments.

4 Comments

Right now I am using references to compensate for this, was just hoping to make the code uniform as in every other class in the Codebase we have Indexers, but in this one, we are using references, Subclasses is really not an option as these variables also Interact with each other and handling that with inheritance is a task I am not willing to Undertake. Shame there isn't a way to do this directly.
@anand_v.singh updated in a note, you can do both of what you describe with the second option
This can be interesting, let me try ;)
The passing down as reference Idea is working in Isolated Sections, I will wait a day and if no better solution comes up, I will start updating the code with this style in mind. Thanks :)
2

I think only a two parameter indexer can achieve what you want.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace ConsoleApp1
{
    class MyClass
    {
        protected static Dictionary<string, FieldInfo[]> table = new Dictionary<string, FieldInfo[]>();
        static public int size = 10;

        protected char[] grades = new char[size];

        public object this[string name, int index]
        {
            get
            {
                var fieldInfos = table[this.GetType().FullName];
                return ((Array)fieldInfos.First((x) => x.Name == name).GetValue(this)).GetValue(index);
            }
            set
            {
                var fieldInfos = table[this.GetType().FullName];
                ((Array)fieldInfos.First((x) => x.Name == name).GetValue(this)).SetValue(value, index);
            }
        }

        static void Main()
        {
            var names = new MyChildClass();
            names[DataColumns.Grades, 1] = 'S';
            names[DataColumns.NameList, 9] = "W.S";
        }
    }

    class MyChildClass : MyClass
    {
        private string[] namelist = new string[size];

        static MyChildClass()
        {
            var t = typeof(MyChildClass);
            table.Add(t.FullName, t.GetFields(BindingFlags.NonPublic | BindingFlags.Instance));
        }

        public MyChildClass()
        {
            for (int i = 0; i < size; i++)
            {
                namelist[i] = "N. A.";
                grades[i] = 'F';
            }
        }
    }

    static class DataColumns
    {
        public static string NameList = "namelist";
        public static string Grades = "grades";
    }
}

4 Comments

I am sorry I am not looking for this as this would soon start making the code unreadable and confusing since actually there are more than two arrays, thanks for the answer though.
@anand_v.singh If you don't mind add extra codes to every child class,why don't you just simply expose every array throught property?
Can you explain the code that you have put here, I think I don't completely understand what you are suggesting here. If possible please rope in the code that I posted in the question.
@anand_v.singh Sorry, I have made some big mistake before. Now I updated my answer with a complete sample.
1

Maybe something like this:

class Indexer
{
    private string[] namelist = new string[size];
    private string[] grades = new string[size + 1]; // size +1 to indicate different 
    // size
    static public int size = 10;

    public void IndexedNames()
    {
        for (int i = 0; i < size; i++)
        {
            namelist[i] = "N. A.";
            grades[i] = "F";
        }
    }

    public string this[int i, int j]
    {
        get
        {
            string tmp;

            // we need to return first array
            if (i > 0)
            {
                tmp = namelist[i];
            }
            else
            {
                tmp = grades[i];
            }

            return (tmp);
        }
        set
        {
            if (i > 0)
            {
                namelist[i] = value;
            }
            else grades[i] = value;
        }
    }
}

1 Comment

I think you misspelled j to be I in your else statements, plus I am sorry I am not looking for this as this would soon start making the code unreadable, thanks for the answer though.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.