You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在C#泛型方法中访问类的类型属性

如何在C#泛型方法中访问类的类型属性

看起来你遇到的问题是泛型方法里没法直接访问特定类的嵌套属性,因为编译器不知道泛型参数T包含SecondaryClass这个成员对吧?别着急,咱们一步步来解决这个问题~

首先先把你的代码整理出来,方便理解场景:

你的原始类定义

public class PrimaryClass
{
    public string PcItemOne { get; set; } = "PC1";
    public string PcItemTwo { get; set; } = "PC2";
    public string PcItemThree { get; set; } = "PC3";
    public SecondaryClass SecondaryClass { get; set; } = new SecondaryClass();
}

public class SecondaryClass
{
    public string ScItemOne { get; set; } = "SC1";
    public string ScItemTwo { get; set; } = "SC2";
    public string ScItemThree { get; set; } = "SC3";
}

有问题的泛型方法

public void CallClassProperty<T>(T cls) where T : class
{
    Console.WriteLine(typeof(T).Name);
    // 这里会编译报错,因为编译器不知道T有SecondaryClass属性
    cls.SecondaryClass.ScItemTwo = "my new value";
}

接下来给你几种可行的解决方案,按推荐程度排序:


方案一:使用接口约束(推荐,类型安全+高性能)

这个方法的核心是给所有包含SecondaryClass属性的类定义一个共同的接口,让泛型参数T继承这个接口,这样编译器就明确知道T包含这个属性了。

  1. 首先定义一个接口,声明SecondaryClass属性:
public interface IHasSecondaryClass
{
    SecondaryClass SecondaryClass { get; set; }
}
  1. 让你的PrimaryClass(以及其他类似的类)实现这个接口:
public class PrimaryClass : IHasSecondaryClass
{
    // 原有成员保持不变
    public string PcItemOne { get; set; } = "PC1";
    public string PcItemTwo { get; set; } = "PC2";
    public string PcItemThree { get; set; } = "PC3";
    public SecondaryClass SecondaryClass { get; set; } = new SecondaryClass();
}
  1. 修改泛型方法的约束,让T必须实现这个接口:
public void CallClassProperty<T>(T cls) where T : class, IHasSecondaryClass
{
    Console.WriteLine(typeof(T).Name);
    // 现在可以直接安全访问了!
    cls.SecondaryClass.ScItemTwo = "my new value";
}

这种方式是最推荐的,因为它有编译时类型检查,能提前发现错误,而且性能和直接访问属性一样好。


方案二:使用反射(备选,适合无法修改原有类的场景)

如果你因为某些原因不能给原有类添加接口(比如类是第三方库的),那可以用反射来动态访问属性。不过反射的缺点是没有编译时检查,性能也稍差,所以尽量少用。

修改后的泛型方法如下:

public void CallClassProperty<T>(T cls) where T : class
{
    Console.WriteLine(typeof(T).Name);
    
    // 获取T类型中的SecondaryClass属性
    var secondaryProp = typeof(T).GetProperty(nameof(PrimaryClass.SecondaryClass));
    if (secondaryProp != null)
    {
        // 获取属性值,转换为SecondaryClass类型
        var secondaryObj = secondaryProp.GetValue(cls) as SecondaryClass;
        if (secondaryObj != null)
        {
            // 获取SecondaryClass中的ScItemTwo属性并设置值
            var scItemTwoProp = typeof(SecondaryClass).GetProperty(nameof(SecondaryClass.ScItemTwo));
            scItemTwoProp?.SetValue(secondaryObj, "my new value");
        }
    }
}

注意:如果属性名拼写错误或者类型不匹配,只会在运行时抛出异常,所以使用时要格外小心。


方案三:使用dynamic(不推荐,类型不安全)

还有一种更简单但风险更高的方式是用dynamic类型,它会跳过编译时检查,直接在运行时解析成员:

public void CallClassProperty<T>(T cls) where T : class
{
    Console.WriteLine(typeof(T).Name);
    dynamic dynamicCls = cls;
    dynamicCls.SecondaryClass.ScItemTwo = "my new value";
}

这种写法虽然简单,但如果传入的T没有SecondaryClass属性,运行时会抛出RuntimeBinderException,所以非常不推荐在生产环境中使用。


总结一下:优先选择接口约束的方案;如果无法修改原有类,再考虑反射dynamic尽量避免使用,除非是快速原型验证之类的场景。

内容来源于stack exchange

火山引擎 最新活动