反射 - 来自深层上下文的 SetValue

如何解决反射 - 来自深层上下文的 SetValue

我正面临一个问题,这肯定是由于我对反射过程缺乏了解,同时试图根据 Json 文件设置“复杂”的类层次结构。

这是我的主要模型:

public class Names
{
    public Weapons Weapons { get; set; }
    public Armors Armors { get; set; }
    public Utilities Utilities { get; set; }
    public Names()
    {
        Weapons = new Weapons();
        Armors = new Armors();
        Utilities = new Utilities();
    }
}

他们每个人都有一个像这样的子模型列表:

public class Weapons
{
    public BattleAxe BattleAxe { get; set; } = new BattleAxe();
    public Bomb_Missile Bomb_Missile { get; set; } = new Bomb_Missile();
    // etc... Around 20 to 25
}

最后的模型与每个 json 文件完全等效,但可能具有非常不同的属性

public class BattleAxe
{
    public string[] normal { get; set; } = new string[0];
    public string[] DescriptiveAdjective { get; set; } = new string[0];
    public string[] Material { get; set; } = new string[0];
    public string[] Type { get; set; } = new string[0];
    public string[] Title { get; set; } = new string[0];
    public string[] Of { get; set; } = new string[0];
    public string[] normalForTitle { get; set; } = new string[0];
}

由于之前 MS Json 反序列化器不支持将 $type 转换为 Newtonsoft,因此我也尝试像这样使用反射来填充值(我已经删除了所有代码可读性的空检查):

public static void Load()
{
    Names = new Names();
    foreach (var category in Names.GetType().GetProperties())
    {
        if (category is not null && !(category.Getgetmethod()?.Isstatic ?? false))
        {
            var categoryType = category.PropertyType;
            foreach (var item in category.PropertyType.GetProperties())
            {
                var itemType = item.PropertyType;
                var subTypeData = JsonSerializer.Deserialize<Dictionary<string,JsonElement>>(File.ReadAllText($"./Assets/Names/{categoryType.Name}/{itemType.Name}.json"));
                var concreteObj = Activator.CreateInstance(itemType);
                foreach (var key in subTypeData.Keys)
                {
                    if (itemType.GetProperty(key) is not null && concreteObj is not null)
                    {
                        var prop = concreteObj.GetType().GetProperty(key);
                        var convertedValue = ConvertJsonType(subTypeData[key],subTypeData[key].ValueKind,out var isReferenceType);
                        // It fails here
                        prop.SetValue(
                            isReferenceType ? convertedValue : null,!isReferenceType ? convertedValue : null
                        );
                    }
                }
                item.SetValue(concreteObj,null);
            }
        }
    }
}

因此它在层次结构中最深对象的 prop.SetValue(...) 处失败,并根据要设置的值类型产生不同的错误。 如果是引用,则抛出 System.Reflection.TargetException : 'Object does not match target type' Exception 如果它是值,它会抛出一个 System.Reflection.TargetException : 'Non-static method requires a target.' 知道我在反序列化方面没有问题,如此处所示,只有我使用动态类型的事实(我的直觉告诉我这实际上是问题......)

debug

我没有添加 ConvertJsonType(...) 主体,因为它很实用而且非常简单

我对“为什么”比“如何”更感兴趣,所以如果你能向我解释问题背后的“理论”,那会很有帮助:)

谢谢!

PS:我知道我可以以更易读/更高效的方式简化事情,但我必须通过反思来实现它以进行个人学习:) System.Text.Json 命名空间相同,我不打算为此切换回 Newtonsoft

解决方法

在调用 SetValue(instance,value) 时,您应该传递应该设置哪个属性的对象。

这是一个疯狂的猜测,但你可以试试这个:

prop.SetValue(concreteObj,!isReferenceType ? convertedValue : null);

因为您要填充 concreteObj 的属性,而不是它本身的值。

如果您查看对象 prop,它的返回值是 concreteObj.GetType().GetProperty(key);。如果你仔细观察,GetProperty 是来自 Type 的一个方法,它没有绑定到任何实例。所以这就是为什么你需要将对象的实例作为第一个参数传递。


我的意思是积极的:每次迭代都会调用 itemType.GetProperty(key),每次迭代都会有相同的值,你可以把它放在循环之前。

,

As docs 状态 TargetException 在以下情况下被抛出:

obj 的类型与目标类型不匹配,或者属性是实例属性但 objnull

当您尝试为静态属性而不是实例设置值时,在 null 中为 obj 传递 SetValue 是有效的。作为引用的属性类型与作为实例或静态属性的属性无关,因此您的调用应该类似于:

prop.SetValue(concreteObj,convertedValue);

此外,您的 item.SetValue(concreteObj,null); 看起来也不正确,因为 concreteObj 应该是此调用中的第二个参数。像这样:

item.SetValue(Names,concreteObj);

此外,如果您只需要实例属性,您可以提供 BindingFlags 以仅获取实例属性:

foreach (var category in Names.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))

我还要说 category is not null 检查是多余的,因此与提供 BindingFlags 配对时,您应该完全删除 if

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?