通过枚举索引器以紧凑的方式访问类成员

时间:2015-06-22 14:31:33

标签: c#

我在C#中寻找一种紧凑的方式来实现这个结构:

FlurryEventRecordStatus.kFlurryEventFailed

该类几乎是一个固定的字段集合,它们共享相同的类型,具有不同的默认参数,并且必须单独访问(通过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; ... } } } } )AND通过索引器。

这将被称为很多,所以反思可能不是一个选项

在C ++中,我可以为这种事做一个宏,但C#没有。

如何在不写重复文字的情况下实现这一目标?

1 个答案:

答案 0 :(得分:4)

你所拥有的CPU使用效率非常高,因此很难被击败。如果您希望在语法方面获得一些效率,可以切换您的设计 - 将数据存储在数组中以方便通过enum访问,并使用属性为外部用户提供漂亮的名称:

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];
        }
    }
}

这种方法有很多代码重复。您可以完全避免使用以下构建重量级设置代码的代价:

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]);
    }
}

此方法在静态构造函数中构建和编译getter和setter,确保反射仅使用一次。请注意,此方法依赖于enum的小写名称对应于字段名称的约定。如果不是这种情况,请替换此行

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

使用其他方式从enum名称中获取字段名称。此代码还假定enum值是连续的,并从零开始。