我正在编写一个 XML 序列化程序,它使用反射递归地爬过对象的公共或私有字段以存储为 XML 并稍后重建,并且在臭名昭著的不可序列化 DataTable 上测试我的序列化程序时,它以某种方式序列化然后尝试重新实例化某种指针,并且在这样做时越过了托管/非托管边界并且(谢天谢地)崩溃而不是乱码内存。我需要找到解决这个问题的方法,但我有点迷茫,因为我没有非托管代码的背景。
我知道您实际上不能按原样序列化指针或引用,因为指针或引用的值是内存地址,并且在重新实例化指针时,您不能期望正确的对象位于该地址XML。据我了解,我要么需要检测一个会导致此问题的对象并忽略它,要么找到并序列化指向的对象,然后在反序列化时对该对象进行反序列化,然后将指针指向所述对象的位置。但我也不知道该怎么做;我的第一个猜测是过滤 Type.IsPointer 似乎并没有解决问题。我要求的可以完成吗?有更好的解决方案吗?我可以用一些战略性的非托管代码来做到这一点吗?
上下文:我正在制作一个序列化程序,它可以序列化普通 XmlSerializer 不能序列化的类型(IDictionary、具有循环引用的类型等)。我的序列化器忽略了属性和 ISerializeable 或 IXMLSerializeable 的实现;它盲目地使用一些规则递归地序列化对象的所有字段。它有效,但它与某些对象猛烈撞击本机/托管边界。我没有使用二进制序列化,因为我的对象不断被修改,而且我不知道如何解决与二进制序列化的对象版本冲突。
编辑:这是在尝试重新实例化“System.Globalization.TextInfo”类时崩溃的代码,我认为这是深埋在 DataTable 某处的文化对象的一部分。这些函数递归地相互调用(总是从 ReInstantiateValueInstance 开始),直到重新实例化初始类型参数。
重新实例化 System.Globalization.TextInfo(CultureInfo) 时,在“bestCtor.Invoke(parameters.ToArray())”处引发托管/本机边界异常
protected object ReCreateTypeWithParameters(Type t)
{
if (t.ToString() == "System.Type") return typeof(object); //we dont know the type of type
var construct = StoreUtilities.GetConstructors(t); //gets any and all constructors for an object
if (construct != null && construct.Count > 0)
{
var leastParams = (from c in construct
select c.GetParameters().Count()).Min();
var bestCtor = (from c in construct
where c.GetParameters().Count() == leastParams
select c).FirstOrDefault(); //the best constructor has the least parameters - less can go wrong
if (bestCtor != null)
{
List<object> parameters = new List<object>();
foreach (var param in bestCtor.GetParameters())
{
parameters.Add(ReInstantiateValueInstance(param.ParameterType));
}
return bestCtor.Invoke(parameters.ToArray()); //pointer types go boom here.
}
}
return null;
}
protected virtual object ReInstantiateValueInstance(Type t)
{
try
{
if (t.ToString() == "System.Type") //we don't know the Type of Type
{
return typeof(object);
}
var construct = StoreUtilities.GetConstructors(t, true); //gets an object's parameterless constructors
if (construct == null && t.IsGenericType) //no constructor, it's generic
{
object generic = ReCreateGenericType(t);
if (generic == null) //if the generic type had no constructor, we use the activator.
{
return Activator.CreateInstance(t);
}
else
{
return generic;
}
}
if (construct == null || construct.Count() == 0) //we have no constuctor. Try and make a placeholder object anyways.
{
return ReCreateTypeWithParameters(t);
}
object o = construct.First().Invoke(null);
return o;
}
catch
{
return null;
}
}
protected object ReCreateGenericType(Type t)
{
try
{
if (Type.IsGenericType != true) return null;
var construct = StoreUtilities.GetConstructors(Type, false);
if (construct != null && construct.Count() > 0)
{
construct = construct.OrderBy(i => i.GetParameters().Count()).ToList();
var tParams = construct[0].GetParameters();
List<object> paramList = new List<object>();
foreach (var p in tParams)
{
if (StoreUtilities.CanStoreAsString(p.ParameterType) == true)
{
object o = Activator.CreateInstance(p.ParameterType);
paramList.Add(o);
}
else
{
paramList.Add(ReInstantiateValueInstance(p.ParameterType));
}
}
return construct[0].Invoke(paramList.ToArray());
}
else
{
return Activator.CreateInstance(t);
}
}
catch
{
return null;
}
}