Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Indexers for multiple Arrays in the class c#

Tags:

c#

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.

like image 234
anand_v.singh Avatar asked Mar 05 '23 07:03

anand_v.singh


2 Answers

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;
like image 159
TheGeneral Avatar answered Mar 09 '23 11:03

TheGeneral


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";
    }
}
like image 24
Alex.Wei Avatar answered Mar 09 '23 09:03

Alex.Wei