C#のオブジェクトがシリアル化可能かどうかを確認する簡単な方法を探しています。
知っているように、ISerializableインターフェースを実装するか、クラスの先頭に[Serializable]を配置することにより、オブジェクトをシリアル化可能にします。
私が探しているのは、クラスを反映して属性を取得することなく、これをチェックする簡単な方法です。インターフェイスは、isステートメントを使用すると迅速になります。
@Flardの提案を使用して、これは私が思いついたコードです、悲鳴はより良い方法があることです。
private static bool IsSerializable(T obj)
{
return ((obj is ISerializable) || (Attribute.IsDefined(typeof (T), typeof (SerializableAttribute))));
}
または、オブジェクトの型を取得して、その型でIsSerializableプロパティを使用することもできます。
typeof(T).IsSerializable
ただし、これには、すべてのクラスをチェックするか、@ pbが指摘したようにエラーをシリアライズして待機する他のクラスがクラスに含まれている場合に、処理しているクラスのみが表示されます。
Type
というIsSerializable
クラスに素敵なプロパティがあります。
直列化可能な属性について、直列化されるオブジェクトのグラフ内のすべてのタイプをチェックする必要があります。最も簡単な方法は、オブジェクトをシリアル化し、例外をキャッチすることです。 (しかし、それは最もクリーンなソリューションではありません)。 Type.IsSerializableと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.IsSerializableは実際に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));
}
この質問に対する答えと here を受け取り、それを修正して、シリアル化できないタイプのリストを取得します。これにより、どのマークを付けるかを簡単に知ることができます。
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);
実行すると、nonSerializableTypesにリストが含まれます。空のListを再帰的メソッドに渡すよりも、これを行うより良い方法があるかもしれません。もしそうなら誰かが私を修正します。
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
例外オブジェクトはシリアライズ可能ですが、そうでない他の例外を使用しています。これは私が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);
}