วิธีตรวจสอบว่าวัตถุสามารถต่ออนุกรมได้ใน C # หรือไม่


94

ฉันกำลังมองหาวิธีง่ายๆในการตรวจสอบว่าวัตถุใน 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 ชี้ไว้


1
ขออภัยที่ล้มเหลวเมื่อฟิลด์ใน obj ไม่สามารถต่อเนื่องได้ดูตัวอย่างของฉัน
Paul van Brenk

ฉันคิดว่านี่เป็นแนวทางที่ดีกว่ามาก: stackoverflow.com/questions/236599/…
xero

คำสั่ง "คุณสร้างออบเจ็กต์ที่สามารถทำให้เป็นอนุกรมได้โดยการใช้อินเทอร์เฟซ ISerializable หรือโดยการวาง [Serializable] ที่ด้านบนสุดของคลาส" เป็นเท็จ เพื่อให้ออบเจ็กต์สามารถต่ออนุกรมได้คลาสของมันต้องประกาศ SerializableAttribute การใช้ ISerializable ทำให้คุณสามารถควบคุมกระบวนการได้มากขึ้นเท่านั้น
Mishax

คำตอบ:


116

คุณมีสถานที่น่าสนใจในระดับที่เรียกว่าTypeIsSerializable


7
สิ่งนี้จะแจ้งให้คุณทราบหากมีการแนบแอตทริบิวต์ของ Serializable เข้ากับชั้นเรียนของคุณ
Fatema

37
ประเด็นของเขาคือสมาชิกของออบเจ็กต์นั้นอาจไม่สามารถต่ออนุกรมได้แม้ว่าประเภทที่มีจะเป็น ขวา? ไม่ใช่กรณีที่เราต้องเจาะลึกลงไปในสมาชิกวัตถุนั้นซ้ำ ๆ และตรวจสอบแต่ละชิ้นถ้าไม่เพียงแค่พยายามทำให้เป็นอนุกรมแล้วดูว่าล้มเหลวหรือไม่?
Brian Sweeney

3
ตัวอย่างเช่นรายการ <SomeDTO> IsSerializable นั้นเป็นจริงแม้ว่า SomeDTO จะไม่สามารถต่ออนุกรมได้ก็ตาม
Simon Dowdeswell

43

คุณจะต้องตรวจสอบทุกประเภทในกราฟของวัตถุที่ถูกทำให้เป็นอนุกรมสำหรับแอตทริบิวต์ที่ทำให้เป็นอนุกรมได้ วิธีที่ง่ายที่สุดคือพยายามทำให้วัตถุเป็นอนุกรมและจับข้อยกเว้น (แต่นั่นไม่ใช่วิธีแก้ปัญหาที่สะอาดที่สุด) 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) นอกจากนี้ออบเจ็กต์อาจมีสมาชิกทั่วไปที่สามารถสลับกับประเภทคลาสที่สืบทอดมาซึ่งอาจทำลายการทำให้เป็นอนุกรมและอาจเปลี่ยนแปลงได้เมื่อรันไทม์ รายการ (ของคลาสพื้นฐาน) อาจมีการเพิ่มรายการของคลาสย่อยซึ่งไม่สามารถทำให้เป็นอนุกรมได้โดยที่คลาสเบสและคลาสย่อยบีสามารถต่ออนุกรมกันได้
VoteCoffee

คำตอบนี้ใช้การโคลนเพื่อตรวจสอบว่าการทำให้เป็นอนุกรมสามารถไป - กลับได้หรือไม่ ในบางกรณีอาจมีการใช้งานมากเกินไปแม้ว่าการทำให้เป็นอนุกรมไม่ได้คาดว่าจะตั้งค่าสมาชิกบางคน: stackoverflow.com/questions/236599/…
VoteCoffee

18

นี่เป็นคำถามเก่าที่อาจต้องอัปเดตสำหรับ. NET 3.5+ Type.IsSerializable สามารถส่งคืนเท็จได้หากคลาสใช้แอตทริบิวต์ DataContract นี่คือตัวอย่างที่ฉันใช้ถ้ามันเหม็นโปรดแจ้งให้เราทราบ :)

public static bool IsSerializable(this object obj)
{
    Type t = obj.GetType();

     return  Attribute.IsDefined(t, typeof(DataContractAttribute)) || t.IsSerializable || (obj is IXmlSerializable)

}

1
คำถามเก่าและคำตอบเก่า แต่นี่เป็นเรื่องจริงมาก! Type.IsSerializable เป็นเพียงโซลูชันที่ใช้งานได้บางส่วน ในความเป็นจริงเมื่อพิจารณาถึงจำนวนการใช้ WCF และ DataContracts ในปัจจุบันมันเป็นทางออกที่แย่มาก!
Jaxidian

จะเกิดอะไรขึ้นถ้า obj มาเป็นโมฆะ?
N73k

@ N73k ทำการnullตรวจสอบและส่งคืนหากtrue?
FredM

9

ใช้ 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

ดังนั้นแม้ว่าคุณจะพิจารณาว่าอินสแตนซ์เฉพาะของประเภทของคุณสามารถต่ออนุกรมกันได้ แต่โดยทั่วไปแล้วคุณไม่สามารถมั่นใจได้ว่าสิ่งนี้จะเป็นจริงกับอินสแตนซ์ทั้งหมด


6
Attribute.IsDefined(typeof (YourClass), typeof (SerializableAttribute));

อาจเกี่ยวข้องกับการสะท้อนใต้น้ำ แต่วิธีที่ง่ายที่สุด?


5

นี่คือรูปแบบ 3.5 ที่ทำให้ทุกคลาสใช้งานได้โดยใช้วิธีการขยาย

public static bool IsSerializable(this object obj)
{
    if (obj is ISerializable)
        return true;
    return Attribute.IsDefined(obj.GetType(), typeof(SerializableAttribute));
}

2

ฉันได้รับคำตอบสำหรับคำถามนี้และคำตอบที่นี่และแก้ไขเพื่อให้คุณได้รับรายชื่อประเภทที่ไม่สามารถต่อเนื่องได้ ด้วยวิธีนี้คุณจะรู้ได้อย่างง่ายดายว่าควรทำเครื่องหมายรายการใด

    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 จะมีรายการ อาจมีวิธีที่ดีกว่าการส่งผ่านรายการว่างไปยังวิธีการเรียกซ้ำ มีคนแก้ไขฉันถ้าเป็นเช่นนั้น


0

อ็อบเจ็กต์ข้อยกเว้นอาจทำให้เป็นอนุกรมได้ แต่ใช้ข้อยกเว้นอื่นที่ไม่ใช่ นี่คือสิ่งที่ฉันเพิ่งมีกับ 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);
    }

0

วิธีแก้ปัญหาของฉันใน 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.