You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何根据泛型类型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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:40:20