Încerc să serializez un obiect Tip în felul următor:
Type myType = typeof (StringBuilder);
var serializer = new XmlSerializer(typeof(Type));
TextWriter writer = new StringWriter();
serializer.Serialize(writer, myType);
Când fac acest lucru, apelul către Serialize aruncă următoarea excepție:
"Tipul System.Text.StringBuilder nu a fost așteptat Atributul XmlInclude sau SoapInclude pentru a specifica tipurile care nu sunt cunoscute în mod static. "
Există o modalitate de a serializa obiectul Type
? Rețineți că nu încerc să serializez codul StringBuilder
, ci obiectul Type
care conține metadatele despre clasa StringBuilder
.
Doar sa uitat la definiția sa, nu este marcată ca Serializabilă. Dacă aveți într-adevăr nevoie ca aceste date să fie serializate, atunci poate fi necesar să le convertiți într-o clasă personalizată care este marcată ca atare.
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)]
Conform documentației MSDN de tip System.Type [1], ar trebui să puteți serializa obiectul System.Type. Cu toate acestea, deoarece eroarea se referă în mod explicit la System.Text.StringBuilder, este probabil clasa care cauzează eroarea de serializare.
[1] Type Class (System) - http://msdn.microsoft.com/en-us/library/system.type.aspx
Nu știam că un obiect Type ar putea fi creat doar cu un șir care conține numele complet calificat. Pentru a obține numele complet calificat, puteți utiliza următoarele:
string typeName = typeof (StringBuilder).FullName;
Puteți persista apoi acest șir oricât de necesar, apoi reconstruiți tipul de genul acesta:
Type t = Type.GetType(typeName);
Dacă aveți nevoie să creați o instanță de tip, puteți face acest lucru:
object o = Activator.CreateInstance(t);
Dacă bifați valoarea o.GetType (), va fi StringBuilder, exact așa cum vă așteptați.
Am avut aceeași problemă, iar soluția mea era să creez o clasă SerializableType. Se convertește liber la și de la System.Type, dar este serializat ca un șir. Tot ce trebuie să faceți este să declarați variabila ca tip Serializable, și de atunci puteți să o referiți la ea ca System.Type.
Aici este clasa:
// 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);
}
}
și un exemplu de utilizare:
[DataContract]
public class A
{
...
[DataMember]
private Dictionary _bees;
...
public B GetB(Type type)
{
return _bees[type];
}
...
}
S-ar putea să considerați, de asemenea, folosirea AssemblyQualifiedName în loc de Type.FullName - a se vedea comentariul de la @GreyCloud
Brian's answer works well if the type is in the same assembly as the call (like GreyCloud pointed out in one of the comments). So if the type is in another assembly you need to use the AssemblyQualifiedName as GreyCloud also pointed out.
Cu toate acestea, deoarece AssemblyQualifiedName salvează versiunea, dacă ansamblurile dvs. au o versiune diferită de cea din șirul în care aveți tipul, nu va funcționa.
În cazul meu a fost o problemă și am rezolvat-o astfel:
string typeName = typeof (MyClass).FullName;
Type type = GetTypeFrom(typeName);
object myInstance = Activator.CreateInstance(type);
Metoda 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;
}
Am postat acest lucru în cazul în care cineva are nevoie de el.