从对象 returns a System.InvalidCastException C# 中转换一个列表 <MyClass>

Casting a List<MyClass> back from object returns a System.InvalidCastException C#

我有一个 Dictionary<string,object> 保存对象以解析到我的插件系统的地方,其中一些对象需要是副本而不是原始对象,所以我使用这个 DeepClone 方法:

public static T DeepCloneJSON<T>(this T Obj)
        {
            var text = JsonSerializer.Serialize(Obj);
            return JsonSerializer.Deserialize<T>(text);
        }

例如,如果我这样做:

var dict = new Dictionary<string,object>(){
    {"someKey",new List<MyClass>(){new MyClass()}}
}
var copy = dict["someKey"].DeepCloneJSON();
var cast = (List<MyClass>)copy;

我得到一个 System.InvalidCastException,但是如果我使用 MemoryStream DeepCopy 方法,我不会得到这个异常,就像这个:

public static T DeepCloneMemoryStream<T>(this T obj)
        {
            using (var ms = new MemoryStream())
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(ms, obj);
                ms.Position = 0;

                return (T)formatter.Deserialize(ms);
            }
        }

所以,我想知道为什么我使用基于 System.text.json 的 DeepClone 方法会出现此异常,是否可以使用 System.text.json 来执行此操作,因为在我的测试中它显示为比基于 onde 的 MemoryStream 更快并且使用更少的内存。

即使 T 是对象,您的 DeepCloneMemoryStream 通过反射重新创建了准确的类型。

您通过 JSON 字符串的 DeepCloneJSON 往返丢失了类型信息,因此反序列化需要指定确切的类型。在您的情况下,您仅将 object 作为 T 传递,因此您返回 JsonElement 而不是 List<MyClass>.

如果您更改拨打电话的方式,以下内容将起作用:

var copy = DeepCloneJSON((List<MyClass>)dict["someKey"]);

或者,更改您的实现,以便根据 Obj 的实际类型而不是指定类型 T 进行反序列化 - 这将与您的其他实现产生类似的效果,其中 T 仅用于铸造。

public static T DeepCloneJSON<T>(T Obj)
{
    var text = JsonSerializer.Serialize(Obj);
    return (T)JsonSerializer.Deserialize(text, Obj.GetType());
}

完全不同的方法

您可以避免转换异常和序列化和反序列化的开销,以及可能出现的问题(例如私有成员、不可序列化的实例等),方法是在使用以下技术的各种开源 NuGet 包中进行选择反射发射或表达式树以避免序列化。

一个例子是评论中建议的 FastDeepCloner。还有其他几个提供类似功能的。