C# 如何使用反射向泛型类型的队列添加对象?

问题描述 投票:0回答:1

我正在尝试使用 C# 为 Godot 中的游戏编写一个通用的保存/加载系统。

我想支持保存具有队列属性的对象。由于游戏代码有很多类,我希望避免为每个不同的类重复相同/相似的保存/加载代码。因此,我有一个主要的 util 类,在保存和加载时处理不同类型的属性。像 int、string、enum 等普通类型都工作正常。

基本上,我有一个

BankAccount
类,它有一个属性
Queue<Expense>

我正在执行以下步骤:

  1. 保存游戏时,我为每个对象创建一个字典,以属性名称为键,以 json 字符串为值。
  2. 如果属性是队列,我将队列的内容转换为(临时)字典,以索引为键,以对象的 json 文本为值,然后使用 json.stringify 将其转换为 json。
  3. 然后使用队列字段的名称将其保存到 BankAccount 字典中。 一切都很好。
  4. 加载后,我反转该过程并首先重新创建 BankAccount 对象,该对象初始化队列属性,以便准备填充。
  5. 我重新创建了字典,其中索引为键,对象的 json 字符串作为值。
  6. 使用 dict 值中的 json 字符串,为需要进入队列的每个索引重新创建费用对象。
  7. 但是,我找不到将创建的 Expense 对象放入队列的方法,因为我无法将初始化的队列属性转换为正确的泛型类型。

如果我将其投射到

Queue
,我会得到:

System.InvalidCastException: Unable to cast object of type 'System.Collections.Generic.Queue`1[scripts.finance.Expense]' to type 'System.Collections.Queue'.

如果我将其投射到

Queue<object>
,我会得到:

 System.InvalidCastException: Unable to cast object of type 'System.Collections.Generic.Queue`1[scripts.finance.Expense]' to type 'System.Collections.Generic.Queue`1[System.Object]'.

这就是我的加载方法的相关部分:

    if( propertyInfo.PropertyType.IsGenericType && 
        propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(Queue<>) ) {
                
        Type genericArgument = propertyInfo.PropertyType.GetGenericArguments()[0];

        Variant variant = Json.ParseString(stringVal);

        Godot.Collections.Dictionary<string, Variant> queueDict = (Godot.Collections.Dictionary<string, Variant>)variant;
            
        foreach (KeyValuePair<string, Variant> pair  in queueDict) {

            string queueElementString = (string)pair.Value;

            object queueObject = CreateDefaultObject(queueElementString);
                    
            Queue<object> queue = (Queue<object>)propertyInfo.GetValue(newObject);

            queue.Enqueue(queueObject);
        }
        return true;
    }

此时,

newObject
是我新创建的
BankAccount
对象,
queueObject
是我新创建的
Expense
对象。但我不知道如何在
Enqueue(queueObject)
对象的
Queue<Expense>
属性上调用
BankAccount

我还尝试获取该方法并调用它,使用:

MethodInfo methodInfo = typeof(Queue).GetMethod("Enqueue");
object invoke = methodInfo.Invoke(propertyInfo.GetValue(newObject), new object[] { queueObject });

但是,在这种情况下我遇到了以下异常:

ERROR: System.Reflection.TargetException: Object does not match target type.

(如果我尝试使用

typeof(Queue<object>).GetMethod("Enqueue");
则相同)

所以,我想了解,有没有一种方法可以在编译时不知道泛型类型的情况下将对象添加到泛型队列中?

如果这是不可能的,还有其他建议如何实现我的游戏加载,而不必为带有队列或列表的每个类实现单独的加载代码吗?

c#
1个回答
0
投票

您是否尝试过构建完整的队列类型?

var queueType = typeof(Queue<>).MakeGenericType(genericArgument);
var myQueue = Activator.CreateInstance(queueType);
var insertMI = queueType.GetMethod("Enqueue");
insertMI.Invoke(myQueue, new[] { queueObject });

但是你将

Queue<object>
Queue<SomeType>
混合在一起,它们是完全不同的,你不能在它们之间进行转换。 相反,你应该使用

Queue<object> queue1 = new();
Queue<SomeType> queue2 = queue1.Cast<SomeType>();

但它会返回一个 IEnumerable

© www.soinside.com 2019 - 2024. All rights reserved.