سؤال

أحاول إجراء تسلسل لكائن النوع بالطريقة التالية:

Type myType = typeof (StringBuilder);
var serializer = new XmlSerializer(typeof(Type));
TextWriter writer = new StringWriter();
serializer.Serialize(writer, myType);

عندما أفعل ذلك، فإن استدعاء التسلسل يؤدي إلى الاستثناء التالي:

"لم يكن النوع System.Text.StringBuilder متوقعًا.استخدم سمة XmlinClude أو SoapinClude لتحديد الأنواع غير المعروفة بشكل ثابت. "

هل هناك طريقة بالنسبة لي لإجراء تسلسل Type هدف؟لاحظ أنني لا أحاول إجراء تسلسل لـ StringBuilder نفسها، ولكن Type كائن يحتوي على البيانات الوصفية حول StringBuilder فصل.

هل كانت مفيدة؟

المحلول

لم أكن أعلم أنه يمكن إنشاء كائن النوع باستخدام سلسلة تحتوي على الاسم المؤهل بالكامل فقط.للحصول على الاسم المؤهل بالكامل، يمكنك استخدام ما يلي:

string typeName = typeof (StringBuilder).FullName;

يمكنك بعد ذلك الاحتفاظ بهذه السلسلة حسب الحاجة، ثم إعادة بناء النوع على النحو التالي:

Type t = Type.GetType(typeName);

إذا كنت بحاجة إلى إنشاء مثيل من النوع، يمكنك القيام بذلك:

object o = Activator.CreateInstance(t);

إذا قمت بفحص قيمة o.GetType()، فستكون StringBuilder، تمامًا كما تتوقع.

نصائح أخرى

واجهت نفس المشكلة، وكان الحل هو إنشاء فئة SerializableType.يقوم بالتحويل بحرية من وإلى System.Type، ولكنه يُجري تسلسلاً كسلسلة.كل ما عليك فعله هو الإعلان عن المتغير باعتباره SerializableType، ومن الآن فصاعدًا يمكنك الإشارة إليه باسم System.Type.

هنا هو الفصل:

// a version of System.Type that can be serialized
[DataContract]
public class SerializableType
{
    public Type type;

    // when serializing, store as a string
    [DataMember]
    string TypeString
    {
        get
        {
            if (type == null)
                return null;
            return type.FullName;
        }
        set
        {
            if (value == null)
                type = null;
            else
            {
                type = Type.GetType(value);
            }
        }
    }

    // constructors
    public SerializableType()
    {
        type = null;
    }
    public SerializableType(Type t)
    {
        type = t;
    }

    // allow SerializableType to implicitly be converted to and from System.Type
    static public implicit operator Type(SerializableType stype)
    {
        return stype.type;
    }
    static public implicit operator SerializableType(Type t)
    {
        return new SerializableType(t);
    }

    // overload the == and != operators
    public static bool operator ==(SerializableType a, SerializableType b)
    {
        // If both are null, or both are same instance, return true.
        if (System.Object.ReferenceEquals(a, b))
        {
            return true;
        }

        // If one is null, but not both, return false.
        if (((object)a == null) || ((object)b == null))
        {
            return false;
        }

        // Return true if the fields match:
        return a.type == b.type;
    }
    public static bool operator !=(SerializableType a, SerializableType b)
    {
        return !(a == b);
    }
    // we don't need to overload operators between SerializableType and System.Type because we already enabled them to implicitly convert

    public override int GetHashCode()
    {
        return type.GetHashCode();
    }

    // overload the .Equals method
    public override bool Equals(System.Object obj)
    {
        // If parameter is null return false.
        if (obj == null)
        {
            return false;
        }

        // If parameter cannot be cast to SerializableType return false.
        SerializableType p = obj as SerializableType;
        if ((System.Object)p == null)
        {
            return false;
        }

        // Return true if the fields match:
        return (type == p.type);
    }
    public bool Equals(SerializableType p)
    {
        // If parameter is null return false:
        if ((object)p == null)
        {
            return false;
        }

        // Return true if the fields match:
        return (type == p.type);
    }
}

ومثال للاستخدام:

[DataContract]
public class A
{

    ...

    [DataMember]
    private Dictionary<SerializableType, B> _bees;

    ...

    public B GetB(Type type)
    {
        return _bees[type];
    }

    ...

}

قد تفكر أيضًا في استخدام AssemblyQualifiedName بدلاً من Type.FullName - راجع تعليق @GreyCloud

بريان تعمل الإجابة بشكل جيد إذا كان النوع موجودًا في نفس تجميع المكالمة (مثل GreyCloud المشار إليه في أحد التعليقات).لذا، إذا كان النوع موجودًا في تجميع آخر، فأنت بحاجة إلى استخدام ملف اسم المؤهل للتجميع كما أشار GreyCloud أيضًا.

ولكن كما اسم المؤهل للتجميع يحفظ الإصدار، إذا كانت التجميعات الخاصة بك تحتوي على إصدار مختلف عن الإصدار الموجود في السلسلة حيث لديك النوع، فلن تعمل.

في حالتي كانت هذه مشكلة وقمت بحلها على النحو التالي:

string typeName = typeof (MyClass).FullName;

Type type = GetTypeFrom(typeName);

object myInstance = Activator.CreateInstance(type);

طريقة GetTypeFrom

private Type GetTypeFrom(string valueType)
    {
        var type = Type.GetType(valueType);
        if (type != null)
            return type;

        try
        {
            var assemblies = AppDomain.CurrentDomain.GetAssemblies();                

            //To speed things up, we check first in the already loaded assemblies.
            foreach (var assembly in assemblies)
            {
                type = assembly.GetType(valueType);
                if (type != null)
                    break;
            }
            if (type != null)
                return type;

            var loadedAssemblies = assemblies.ToList();

            foreach (var loadedAssembly in assemblies)
            {
                foreach (AssemblyName referencedAssemblyName in loadedAssembly.GetReferencedAssemblies())
                {
                    var found = loadedAssemblies.All(x => x.GetName() != referencedAssemblyName);

                    if (!found)
                    {
                        try
                        {
                            var referencedAssembly = Assembly.Load(referencedAssemblyName);
                            type = referencedAssembly.GetType(valueType);
                            if (type != null)
                                break;
                            loadedAssemblies.Add(referencedAssembly);
                        }
                        catch
                        {
                            //We will ignore this, because the Type might still be in one of the other Assemblies.
                        }
                    }
                }
            }                
        }
        catch(Exception exception)
        {
            //throw my custom exception    
        }

        if (type == null)
        {
            //throw my custom exception.
        }

        return type;
    }

أنا أنشر هذا في حال احتاجه أي شخص.

وفقًا لوثائق MSDN الخاصة بـ System.Type [1]، يجب أن تكون قادرًا على إجراء تسلسل لكائن System.Type.ومع ذلك، نظرًا لأن الخطأ يشير بشكل صريح إلى System.Text.StringBuilder، فمن المحتمل أن تكون هذه هي الفئة التي تسبب خطأ التسلسل.

[1] فئة النوع (النظام) - http://msdn.microsoft.com/en-us/library/system.type.aspx

بمجرد إلقاء نظرة على تعريفه، لم يتم وضع علامة عليه على أنه قابل للتسلسل.إذا كنت تريد حقًا إجراء تسلسل لهذه البيانات، فقد يتعين عليك تحويلها إلى فئة مخصصة تم وضع علامة عليها على هذا النحو.

public abstract class Type : System.Reflection.MemberInfo
    Member of System

Summary:
Represents type declarations: class types, interface types, array types, value types, enumeration types, type parameters, generic type definitions, and open or closed constructed generic types.

Attributes:
[System.Runtime.InteropServices.ClassInterfaceAttribute(0),
System.Runtime.InteropServices.ComDefaultInterfaceAttribute(System.Runtime.InteropServices._Type),
System.Runtime.InteropServices.ComVisibleAttribute(true)]

لقد واجهت هذه المشكلة أثناء محاولة إجراء تسلسل ثنائي في .net Standard 2.0.انتهى بي الأمر إلى حل المشكلة باستخدام Custom SurrogateSelector و SerializationBinder.

ال TypeSerializationBinder كان مطلوبًا لأن إطار العمل كان يواجه مشكلة في الحل System.RuntimeType قبل أن يحصل SurrogateSelector.لا أفهم حقًا سبب ضرورة حل النوع قبل هذه الخطوة ...

هنا هو الرمز:

// Serializes and deserializes System.Type
public class TypeSerializationSurrogate : ISerializationSurrogate {
    public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) {
        info.AddValue(nameof(Type.FullName), (obj as Type).FullName);
    }

    public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) {
        return Type.GetType(info.GetString(nameof(Type.FullName)));
    }
}

// Just a stub, doesn't need an implementation
public class TypeStub : Type { ... }

// Binds "System.RuntimeType" to our TypeStub
public class TypeSerializationBinder : SerializationBinder {
    public override Type BindToType(string assemblyName, string typeName) {
        if(typeName == "System.RuntimeType") {
            return typeof(TypeStub);
        }
        return Type.GetType($"{typeName}, {assemblyName}");
    }
}

// Selected out TypeSerializationSurrogate when [de]serializing Type
public class TypeSurrogateSelector : ISurrogateSelector {
    public virtual void ChainSelector(ISurrogateSelector selector) => throw new NotSupportedException();

    public virtual ISurrogateSelector GetNextSelector() => throw new NotSupportedException();

    public virtual ISerializationSurrogate GetSurrogate(Type type, StreamingContext context, out ISurrogateSelector selector) {
        if(typeof(Type).IsAssignableFrom(type)) {
            selector = this;
            return new TypeSerializationSurrogate();
        }
        selector = null;
        return null;
    }
}

مثال الاستخدام:

byte[] bytes
var serializeFormatter = new BinaryFormatter() {
    SurrogateSelector = new TypeSurrogateSelector()
}
using (var stream = new MemoryStream()) {
    serializeFormatter.Serialize(stream, typeof(string));
    bytes = stream.ToArray();
}

var deserializeFormatter = new BinaryFormatter() {
    SurrogateSelector = new TypeSurrogateSelector(),
    Binder = new TypeDeserializationBinder()
}
using (var stream = new MemoryStream(bytes)) {
    type = (Type)deserializeFormatter .Deserialize(stream);
    Assert.Equal(typeof(string), type);
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top