如何根据泛型类型T获取MyClass中对应IMyInterface<T>属性?
实现类似EF风格的泛型属性访问方法
嘿,这个需求和EF里DbSet<T>的思路简直一模一样,咱们可以根据你的实际场景选不同的实现方式,下面给你几个实用的方案:
首先先把你定义的基础代码贴出来方便对照:
public interface IMyInterface<T> { } public class Foo { } public class Bar { } public class MyClass { public IMyInterface<Foo> Foos {get; set;} public IMyInterface<Bar> Bars {get; set;} }
方案1:固定类型分支判断(简单高效,适合类型不常变的场景)
如果你的Foo、Bar这类类型是固定的,不会频繁新增,直接写类型判断是最直观的,性能也最高:
public class MyClass { public IMyInterface<Foo> Foos { get; set; } public IMyInterface<Bar> Bars { get; set; } // 实例方法版本:直接从当前实例返回对应属性 public IMyInterface<T> Interfaces<T>() { if (typeof(T) == typeof(Foo)) return (IMyInterface<T>)Foos; if (typeof(T) == typeof(Bar)) return (IMyInterface<T>)Bars; throw new ArgumentException($"找不到对应类型 {typeof(T)} 的属性"); } // 静态方法版本:需要传入MyClass实例(和EF的静态调用风格更像) public static IMyInterface<T> Interfaces<T>(MyClass instance) { if (instance == null) throw new ArgumentNullException(nameof(instance)); return instance.Interfaces<T>(); } }
使用起来也很简单:
var myInstance = new MyClass(); // 实例方法调用 var fooInterface = myInstance.Interfaces<Foo>(); // 静态方法调用 var barInterface = MyClass.Interfaces<Bar>(myInstance);
这个方案的优点是没有性能损耗,缺点是如果后续要加新的IMyInterface<T>属性,得手动给方法加新的判断分支。
方案2:反射实现(动态扩展,适合类型可能新增的场景)
如果后续可能会不断新增类似Foos、Bars的属性,用反射可以自动识别符合类型的属性,不用每次改代码:
public class MyClass { public IMyInterface<Foo> Foos { get; set; } public IMyInterface<Bar> Bars { get; set; } public IMyInterface<T> Interfaces<T>() { // 查找当前类中类型为IMyInterface<T>的公共属性 var targetProperty = GetType().GetProperties() .FirstOrDefault(p => p.PropertyType == typeof(IMyInterface<T>)); if (targetProperty == null) throw new ArgumentException($"找不到类型为 IMyInterface<{typeof(T)}> 的属性"); return (IMyInterface<T>)targetProperty.GetValue(this); } }
解释:这个方法会自动扫描类中所有公共属性,找到第一个类型匹配IMyInterface<T>的属性并返回。如果有多个同类型的属性(比如两个IMyInterface<Foo>),可以自己加逻辑过滤,比如按属性名称匹配(比如找名称包含typeof(T).Name的)。
缺点是反射有一点点性能开销,如果是高频调用的场景,建议加个缓存优化。
方案3:表达式树缓存(兼顾动态和高性能)
想要动态扩展的同时又不想有反射的性能损耗?可以用表达式树提前编译属性访问的委托,第一次调用后缓存起来,后续调用就和直接访问属性一样快:
public class MyClass { public IMyInterface<Foo> Foos { get; set; } public IMyInterface<Bar> Bars { get; set; } // 缓存编译好的属性访问委托 private static readonly Dictionary<Type, Delegate> _propertyAccessors = new Dictionary<Type, Delegate>(); public IMyInterface<T> Interfaces<T>() { var typeKey = typeof(T); if (!_propertyAccessors.TryGetValue(typeKey, out var accessor)) { // 找到目标属性 var targetProperty = GetType().GetProperties() .FirstOrDefault(p => p.PropertyType == typeof(IMyInterface<T>)); if (targetProperty == null) throw new ArgumentException($"找不到类型为 IMyInterface<{typeKey}> 的属性"); // 构建表达式树并编译成委托 var instanceParam = Expression.Parameter(typeof(MyClass), "instance"); var propertyExpr = Expression.Property(instanceParam, targetProperty); var convertExpr = Expression.Convert(propertyExpr, typeof(IMyInterface<T>)); accessor = Expression.Lambda<Func<MyClass, IMyInterface<T>>>(convertExpr, instanceParam).Compile(); // 线程安全的方式加入缓存 lock (_propertyAccessors) { if (!_propertyAccessors.ContainsKey(typeKey)) _propertyAccessors[typeKey] = accessor; } } // 调用缓存的委托获取属性值 return ((Func<MyClass, IMyInterface<T>>)accessor)(this); } }
这个方案完美兼顾了动态扩展和高性能:第一次调用某个T时会编译委托,之后再调用就直接用缓存的委托,性能和直接访问属性几乎无差别。
内容的提问来源于stack exchange,提问作者Jose Belisario
相关产品推荐
相关产品推荐

