题
我正在寻找一种简单的方法来检查 C# 中的对象是否可序列化。
正如我们所知,您可以通过实现以下方式使对象可序列化 可序列化 接口或通过放置 [可序列化] 在班级中名列前茅。
我正在寻找一种快速的方法来检查这一点,而不必反映该类来获取其属性。使用该界面会很快 是 陈述。
使用@Flard的建议,这是我想出的代码,尖叫是否有更好的方法。
private static bool IsSerializable(T obj)
{
return ((obj is ISerializable) || (Attribute.IsDefined(typeof (T), typeof (SerializableAttribute))));
}
或者更好的是获取对象的类型,然后在该类型上使用 IsSerialized 属性:
typeof(T).IsSerializable
请记住,虽然这似乎只是我们正在处理的类,但如果该类包含其他类,您可能想检查所有这些类,或者尝试序列化并等待错误,如 @pb 指出的那样。
解决方案
你有一个可爱的财产 Type
类称为 IsSerializable
.
其他提示
您必须检查正在序列化的对象图中的所有类型的可序列化属性。最简单的方法是尝试序列化对象并捕获异常。(但这不是最干净的解决方案)。Type.IsSerialized 和检查serializalbe 属性不考虑图表。
样本
[Serializable]
public class A
{
public B B = new B();
}
public class B
{
public string a = "b";
}
[Serializable]
public class C
{
public D D = new D();
}
[Serializable]
public class D
{
public string d = "D";
}
class Program
{
static void Main(string[] args)
{
var a = typeof(A);
var aa = new A();
Console.WriteLine("A: {0}", a.IsSerializable); // true (WRONG!)
var c = typeof(C);
Console.WriteLine("C: {0}", c.IsSerializable); //true
var form = new BinaryFormatter();
// throws
form.Serialize(new MemoryStream(), aa);
}
}
这是一个老问题,可能需要针对 .NET 3.5+ 进行更新。如果类使用 DataContract 属性,Type.IsSerialized 实际上可以返回 false。这是我使用的一个片段,如果它很臭,请告诉我:)
public static bool IsSerializable(this object obj)
{
Type t = obj.GetType();
return Attribute.IsDefined(t, typeof(DataContractAttribute)) || t.IsSerializable || (obj is IXmlSerializable)
}
正如其他人指出的那样,使用 Type.IsSerializable 。
可能不值得尝试反映和检查对象图中的所有成员是否都是可序列化的。
成员可以声明为可序列化类型,但实际上被实例化为不可序列化的派生类型,如以下人为示例所示:
[Serializable]
public class MyClass
{
public Exception TheException; // serializable
}
public class MyNonSerializableException : Exception
{
...
}
...
MyClass myClass = new MyClass();
myClass.TheException = new MyNonSerializableException();
// myClass now has a non-serializable member
因此,即使您确定类型的特定实例是可序列化的,通常也不能确定所有实例都是如此。
Attribute.IsDefined(typeof (YourClass), typeof (SerializableAttribute));
可能涉及水下反射,但最简单的方法是什么?
这是 3.5 的变体,它可以使用扩展方法供所有类使用。
public static bool IsSerializable(this object obj)
{
if (obj is ISerializable)
return true;
return Attribute.IsDefined(obj.GetType(), typeof(SerializableAttribute));
}
我接受了这个问题的答案和答案 这里 并修改它,以便您获得不可序列化的类型列表。这样您就可以轻松知道要标记哪些。
private static void NonSerializableTypesOfParentType(Type type, List<string> nonSerializableTypes)
{
// base case
if (type.IsValueType || type == typeof(string)) return;
if (!IsSerializable(type))
nonSerializableTypes.Add(type.Name);
foreach (var propertyInfo in type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
if (propertyInfo.PropertyType.IsGenericType)
{
foreach (var genericArgument in propertyInfo.PropertyType.GetGenericArguments())
{
if (genericArgument == type) continue; // base case for circularly referenced properties
NonSerializableTypesOfParentType(genericArgument, nonSerializableTypes);
}
}
else if (propertyInfo.GetType() != type) // base case for circularly referenced properties
NonSerializableTypesOfParentType(propertyInfo.PropertyType, nonSerializableTypes);
}
}
private static bool IsSerializable(Type type)
{
return (Attribute.IsDefined(type, typeof(SerializableAttribute)));
//return ((type is ISerializable) || (Attribute.IsDefined(type, typeof(SerializableAttribute))));
}
然后你就称它为...
List<string> nonSerializableTypes = new List<string>();
NonSerializableTypesOfParentType(aType, nonSerializableTypes);
当它运行时,nonSerializedTypes 将拥有该列表。可能有比将空列表传递给递归方法更好的方法。如果是的话有人纠正我。
异常对象可能是可序列化的,但使用了另一个不可序列化的异常。这是我刚刚使用 WCF System.ServiceModel.FaultException 得到的结果:FaultException 是可序列化的,但 ExceptionDetail 不是!
所以我正在使用以下内容:
// Check if the exception is serializable and also the specific ones if generic
var exceptionType = ex.GetType();
var allSerializable = exceptionType.IsSerializable;
if (exceptionType.IsGenericType)
{
Type[] typeArguments = exceptionType.GetGenericArguments();
allSerializable = typeArguments.Aggregate(allSerializable, (current, tParam) => current & tParam.IsSerializable);
}
if (!allSerializable)
{
// Create a new Exception for not serializable exceptions!
ex = new Exception(ex.Message);
}
我的解决方案,在 VB.NET 中:
对于对象:
''' <summary>
''' Determines whether an object can be serialized.
''' </summary>
''' <param name="Object">The object.</param>
''' <returns><c>true</c> if object can be serialized; otherwise, <c>false</c>.</returns>
Private Function IsObjectSerializable(ByVal [Object] As Object,
Optional ByVal SerializationFormat As SerializationFormat =
SerializationFormat.Xml) As Boolean
Dim Serializer As Object
Using fs As New IO.MemoryStream
Select Case SerializationFormat
Case Data.SerializationFormat.Binary
Serializer = New Runtime.Serialization.Formatters.Binary.BinaryFormatter()
Case Data.SerializationFormat.Xml
Serializer = New Xml.Serialization.XmlSerializer([Object].GetType)
Case Else
Throw New ArgumentException("Invalid SerializationFormat", SerializationFormat)
End Select
Try
Serializer.Serialize(fs, [Object])
Return True
Catch ex As InvalidOperationException
Return False
End Try
End Using ' fs As New MemoryStream
End Function
对于类型:
''' <summary>
''' Determines whether a Type can be serialized.
''' </summary>
''' <typeparam name="T"></typeparam>
''' <returns><c>true</c> if Type can be serialized; otherwise, <c>false</c>.</returns>
Private Function IsTypeSerializable(Of T)() As Boolean
Return Attribute.IsDefined(GetType(T), GetType(SerializableAttribute))
End Function
''' <summary>
''' Determines whether a Type can be serialized.
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="Type">The Type.</param>
''' <returns><c>true</c> if Type can be serialized; otherwise, <c>false</c>.</returns>
Private Function IsTypeSerializable(Of T)(ByVal Type As T) As Boolean
Return Attribute.IsDefined(GetType(T), GetType(SerializableAttribute))
End Function