C#:不修改原有属性,用有序列表替代反射访问类属性
替代反射访问遗留类属性的优化方案
针对你提到的遗留MyObj类,既要保留原有Pro1至Pro8属性的兼容性,又要实现列表式索引访问,这里提供几种比switch-case更优的方案:
方案1:手动委托索引器(性能最优)
直接在MyObj类中添加索引器,通过预定义的委托数组映射属性的get/set操作,完全避免反射,性能接近直接访问属性:
public class MyObj { // 原有属性保留不动,兼容系统其他依赖 public string Pro1 { get; set; } public string Pro2 { get; set; } public string Pro3 { get; set; } public string Pro4 { get; set; } public string Pro5 { get; set; } public string Pro6 { get; set; } public string Pro7 { get; set; } public string Pro8 { get; set; } // 实现列表式索引访问 public string this[int index] { get { if (index < 0 || index >= _getters.Length) throw new ArgumentOutOfRangeException(nameof(index)); return _getters[index](); } set { if (index < 0 || index >= _setters.Length) throw new ArgumentOutOfRangeException(nameof(index)); _setters[index](value); } } // 预初始化属性访问委托数组 private readonly Func<string>[] _getters; private readonly Action<string>[] _setters; public MyObj() { _getters = new Func<string>[] { () => Pro1, () => Pro2, () => Pro3, () => Pro4, () => Pro5, () => Pro6, () => Pro7, () => Pro8 }; _setters = new Action<string>[] { val => Pro1 = val, val => Pro2 = val, val => Pro3 = val, val => Pro4 = val, val => Pro5 = val, val => Pro6 = val, val => Pro7 = val, val => Pro8 = val }; } }
使用方式:
var obj = new MyObj(); obj[0] = "赋值给Pro1"; // 对应Pro1 var pro4Value = obj[3]; // 获取Pro4的值
方案2:表达式树预编译(适配属性扩展场景)
如果后续可能新增Pro9这类属性,手动维护委托数组太麻烦,可以用表达式树在静态构造中一次性生成委托,仅在类首次加载时做一次反射操作,后续访问无反射开销:
public class MyObj { // 原有属性保留不动 public string Pro1 { get; set; } public string Pro2 { get; set; } public string Pro3 { get; set; } public string Pro4 { get; set; } public string Pro5 { get; set; } public string Pro6 { get; set; } public string Pro7 { get; set; } public string Pro8 { get; set; } public string this[int index] { get => _getters[index](this); set => _setters[index](this, value); } private static readonly Func<MyObj, string>[] _getters; private static readonly Action<MyObj, string>[] _setters; static MyObj() { // 自动按规则筛选并排序Pro属性 var properties = typeof(MyObj) .GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => p.Name.StartsWith("Pro") && int.TryParse(p.Name.Substring(3), out _)) .OrderBy(p => int.Parse(p.Name.Substring(3))) .ToArray(); // 生成get委托 _getters = properties.Select(p => { var param = Expression.Parameter(typeof(MyObj)); var propertyAccess = Expression.Property(param, p); return Expression.Lambda<Func<MyObj, string>>(propertyAccess, param).Compile(); }).ToArray(); // 生成set委托 _setters = properties.Select(p => { var objParam = Expression.Parameter(typeof(MyObj)); var valueParam = Expression.Parameter(typeof(string)); var propertyAccess = Expression.Property(objParam, p); var assign = Expression.Assign(propertyAccess, valueParam); return Expression.Lambda<Action<MyObj, string>>(assign, objParam, valueParam).Compile(); }).ToArray(); } }
方案3:包装类(不修改原类场景)
如果无法修改MyObj类(比如类是密封的或属于第三方库),可以写一个包装类实现列表式访问:
public class MyObjListWrapper { private readonly MyObj _target; public MyObjListWrapper(MyObj target) { _target = target ?? throw new ArgumentNullException(nameof(target)); } public string this[int index] { get => _getters[index](_target); set => _setters[index](_target, value); } private static readonly Func<MyObj, string>[] _getters = new[] { obj => obj.Pro1, obj => obj.Pro2, obj => obj.Pro3, obj => obj.Pro4, obj => obj.Pro5, obj => obj.Pro6, obj => obj.Pro7, obj => obj.Pro8 }; private static readonly Action<MyObj, string>[] _setters = new[] { (obj, val) => obj.Pro1 = val, (obj, val) => obj.Pro2 = val, (obj, val) => obj.Pro3 = val, (obj, val) => obj.Pro4 = val, (obj, val) => obj.Pro5 = val, (obj, val) => obj.Pro6 = val, (obj, val) => obj.Pro7 = val, (obj, val) => obj.Pro8 = val }; }
使用方式:
var obj = new MyObj(); var wrapper = new MyObjListWrapper(obj); wrapper[0] = "赋值给Pro1";
内容的提问来源于stack exchange,提问作者qkhanhpro
相关产品推荐
相关产品推荐

