从对象 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。还有其他几个提供类似功能的。
我有一个 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。还有其他几个提供类似功能的。