Accessing class classes using an enumeration indexer in compact mode

I'm looking for a compact way in C # to implement this construct:

public class Test {
    public enum Field {
        A, B, C, D, E...
    }

    public int a = 0;
    public int b = 0;
    public int c = 0;
    public int d = 0;
    public int e = 0; //....

    public int this[Field field] {
        set{
            switch(field) {
                case(Field.A):
                    a = value;
                    break;
                case(Field.B):
                    b = value;
                    break;
                ...
            }
        }

        get{
            switch(field) {
                case(Field.A):
                    return a;
                case(Field.B):
                    return b;
                ...
            }
        }
    }
}

      

A class is a fairly fixed set of fields that are of the same type, have different default parameters, and must be accessed separately (via .a

) and using the indexer.

This will be called LOT, so reflection is probably not an option

In C ++, I could make a macro for this kind of thing, but C # doesn't.

How can I implement this without writing a wall of repeating text?

+3


source to share


1 answer


What you have is quite efficient in terms of CPU usage, so it is very difficult to beat it. If you want some efficiency in terms of syntax, you can switch your design around - store your data in an array for easy accessibility through enum

and use properties to provide attractive names to external users:

public class Test {
    public enum Field {
        A, B, C, D, E..., X, Y, Z
    }
    private readonly int[] data = new int[(int)(Field.Z + 1)];
    public int A {
        get {
            return data[Field.A];
        }
        set {
            data[Field.A] = value;
        }
    }
    //...
    public int Z {
        get {
            return data[Field.Z];
        }
        set {
            data[Field.Z] = value;
        }
    }
    public int this[Field field] {
        set{
            data[field] = value;
        }

        get{
            return data[field];
        }
    }
}

      

There is a lot of repetition in this approach. You could have avoided this altogether at the cost of generating some cumbersome setup code below:

class Program {
    public enum Field { A, B, C };
    public int a, b, c;
    private static readonly Func<Program,int>[] Getter= new Func<Program,int>[3];
    private static readonly Action<Program,int>[] Setter= new Action<Program,int>[3];
    public int this[Field f] {
        get {
            return Getter[(int)f](this);
        }
        set {
            Setter[(int) f](this, value);
        }
    }
    static Program() {
        var names = Enum.GetNames(typeof(Field));
        var pThis = Expression.Parameter(typeof(Program), "pThis");
        var pVal = Expression.Parameter(typeof(int), "pVal");
        for (var i = 0 ; i != names.Length ; i++) {
            var f = Expression.Field(pThis, names[i].ToLower());
            Getter[i] = Expression.Lambda<Func<Program, int>>(f, pThis).Compile();
            var a = Expression.Assign(f, pVal);
            Setter[i] = Expression.Lambda<Action<Program,int>>(a, pThis, pVal).Compile();
        }
    }
    public override string ToString() {
        return string.Format("a={0} b={1} c={2}", a, b, c);
    }
    private static void Main(string[] args) {
        var p = new Program();
        p.a = 123;
        p.b = 456;
        p.c = 789;
        Console.WriteLine(p);
        p[Field.A] = 234;
        p[Field.B] = 567;
        p[Field.C] = 890;
        Console.WriteLine(p);
        Console.WriteLine(p[Field.A]);
        Console.WriteLine(p[Field.B]);
        Console.WriteLine(p[Field.C]);
    }
}

      



This approach builds and compiles getters and setters in a static constructor, making sure that reflection is only used once. Note that this approach is based on the agreement that the subcategory names enum

match the field names. If not, replace this line

Expression.Field(pThis, names[i].ToLower());

      

with a different way of getting the field enum

name from name. This code also assumes that the values enum

are sequential and start at zero.

+4


source







All Articles