Я ищу простой способ проверить, сериализуем ли объект в C#.
Как мы знаем, вы делаете объект сериализуемым, реализуя интерфейс ISerializable или помещая [Сериализуемый] в начало класса.
Я ищу быстрый способ проверить это без необходимости отражать класс, чтобы получить его атрибуты. Интерфейс был бы быстрым, используя оператор является.
Используя предложение @Flard, это код, который я придумал, крик, есть ли лучший способ.
private static bool IsSerializable(T obj)
{
return ((obj is ISerializable) || (Attribute.IsDefined(typeof (T), typeof (SerializableAttribute))));
}
Или, что еще лучше, просто получите тип объекта и затем используйте свойство IsSerializable для этого типа:
typeof(T).IsSerializable
Помните, хотя это кажется только классом, с которым мы имеем дело, если класс содержит другие классы, вы, вероятно, захотите проверить их все или попытаться сериализовать и дождаться ошибок, как указал @pb.
Я думаю, что это намного лучший подход: stackoverflow.com/questions/236599/…
Утверждение «вы делаете объект сериализуемым, реализуя интерфейс ISerializable или помещая [Serializable] в начало класса», неверно. Чтобы объект можно было сериализовать, в его классе должен быть объявлен атрибут SerializableAttribute. Внедрение ISerializable только дает вам больший контроль над процессом.





Attribute.IsDefined(typeof (YourClass), typeof (SerializableAttribute));
Наверное, предполагает отражение под водой, но самый простой способ?
Вам нужно будет проверить все типы в графе сериализуемых объектов на предмет сериализуемого атрибута. Самый простой способ - попытаться сериализовать объект и перехватить исключение. (Но это не самое чистое решение). 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);
}
}
Если стоимость не слишком велика, я думаю, что этот подход является лучшим. Он может проверять различные требования к сериализации (двоичный, xml). Кроме того, у объекта может быть общий член, который может быть заменен унаследованными типами классов, которые могут нарушать сериализацию и могут изменяться во время выполнения. Список (Of baseclass) может иметь добавленные элементы подкласса A, который не является сериализуемым, где базовый класс и подклассB сериализуемы.
В этом ответе используется клонирование, чтобы проверить, может ли сериализация проходить туда и обратно. В некоторых случаях это может быть излишним, хотя не ожидается, что сериализация установит некоторые члены: stackoverflow.com/questions/236599/…
У вас есть прекрасный отель класса Type под названием IsSerializable.
Это просто проинформирует вас, прикреплен ли к вашему классу атрибут Serializable.
его точка зрения состоит в том, что члены этого объекта могут быть не сериализуемыми, даже если содержащий тип. верно? Разве это не тот случай, когда мы должны рекурсивно углубляться в элементы этих объектов и проверять каждый из них, если не просто пытаться сериализовать его и посмотреть, не сработает ли он?
Например, для List <SomeDTO> IsSerializable истинно, даже если SomeDTO НЕ сериализуем.
Вот вариант 3.5, который делает его доступным для всех классов, использующих метод расширения.
public static bool IsSerializable(this object obj)
{
if (obj is ISerializable)
return true;
return Attribute.IsDefined(obj.GetType(), typeof(SerializableAttribute));
}
Используйте 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
Следовательно, даже если вы определите, что конкретный экземпляр вашего типа является сериализуемым, вы, как правило, не можете быть уверены, что это будет верно для всех экземпляров.
Это старый вопрос, который, возможно, потребуется обновить для .NET 3.5+. Type.IsSerializable может фактически вернуть false, если класс использует атрибут DataContract. Вот фрагмент, который я использую, если он воняет, дайте мне знать :)
public static bool IsSerializable(this object obj)
{
Type t = obj.GetType();
return Attribute.IsDefined(t, typeof(DataContractAttribute)) || t.IsSerializable || (obj is IXmlSerializable)
}
Старый вопрос и старые ответы, но это ОЧЕНЬ верно! Type.IsSerializable - это частично функциональное решение. Фактически, учитывая, сколько людей используют WCF и DataContracts в наши дни, это на самом деле очень плохое решение!
Что, если obj имеет значение null?
@ N73k сделать проверку null и вернуть, если true?
Объект исключения может быть сериализуемым, но с использованием другого исключения, которого нет. Это то, что у меня было только что с 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);
}
Я взял ответ на этот вопрос и ответ здесь и изменил его, чтобы вы получили список типов, которые не сериализуемы. Таким образом, вы легко узнаете, какие из них отметить.
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 будет иметь список. Может быть лучший способ сделать это, чем передать пустой список рекурсивному методу. Кто-нибудь поправьте меня, если так.
Мое решение в 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
Мне это нравится, потому что, как отметили другие респонденты, атрибуты и свойства ничего не значат. Вы можете прикрепить атрибут ISerializable к любому классу, Type.IsSerializable неисправен, но самое главное: объекты, на которые он ссылается, могут фактически быть несериализуемыми подклассами. Единственный способ - попробовать. Но я бы немного доработал ее до функции TrySerialize, чтобы успешную сериализацию не нужно было выполнять дважды.
Извините, это не удается, когда поле в obj не сериализуемо, см. Мой образец.