Ich bin endlich einem Problem auf den Grund gegangen und frage mich, was mein bester Rückgriff ist. Kurz gesagt, das Problem besteht darin, dass XNAs ReflectiveReader
in generische Typparameter reflektiert werden, selbst wenn keine Instanz dieses generischen Typs in dem zu serialisierenden Objekt gespeichert ist.
Ein Beispiel zeigt dies am besten. Betrachten Sie die folgenden Modellklassen:
namespace Model
{
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
public abstract class Entity
{
}
public sealed class TestEntity : Entity
{
public Texture2D Texture
{
get;
set;
}
}
public abstract class EntityData
{
}
public abstract class EntityData<TData, TEntity> : EntityData
where TData : EntityData
where TEntity : Entity
{
}
public sealed class TestEntityData : EntityData<TestEntityData, TestEntity>
{
}
public sealed class LevelData
{
public List<EntityData> Entities
{
get;
set;
}
}
}
Angenommen, ich möchte eine Instanz von LevelData in einer XML-Datei definieren, die später mit ContentManager
( Test.xml ) geladen werden soll :
<?xml version="1.0" encoding="utf-8"?>
<XnaContent xmlns:Model="Model">
<Asset Type="Model:LevelData">
<Entities>
<Item Type="Model:TestEntityData">
</Item>
</Entities>
</Asset>
</XnaContent>
Betrachten Sie nun diese einfache Ladelogik:
Content.Load<LevelData>("Test");
Content.Load<Texture2D>("Texture");
Die erste Zeile ist erfolgreich, die zweite löst jedoch eine Ausnahme aus:
Microsoft.Xna.Framework.Content.ContentLoadException was unhandled
Message=Error loading "Texture". ContentTypeReader Microsoft.Xna.Framework.Content.Texture2DReader, Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553 conflicts with existing handler Microsoft.Xna.Framework.Content.ReflectiveReader`1[[Microsoft.Xna.Framework.Graphics.Texture2D, Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553]], Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553 for type Microsoft.Xna.Framework.Graphics.Texture2D.
Source=Microsoft.Xna.Framework
StackTrace:
at Microsoft.Xna.Framework.Content.ContentTypeReaderManager.AddTypeReader(String readerTypeName, ContentReader contentReader, ContentTypeReader reader)
at Microsoft.Xna.Framework.Content.ContentTypeReaderManager.GetTypeReader(String readerTypeName, ContentReader contentReader, List`1& newTypeReaders)
at Microsoft.Xna.Framework.Content.ContentTypeReaderManager.ReadTypeManifest(Int32 typeCount, ContentReader contentReader)
at Microsoft.Xna.Framework.Content.ContentReader.ReadHeader()
at Microsoft.Xna.Framework.Content.ContentReader.ReadAsset[T]()
at Microsoft.Xna.Framework.Content.ContentManager.ReadAsset[T](String assetName, Action`1 recordDisposableObject)
at Microsoft.Xna.Framework.Content.ContentManager.Load[T](String assetName)
at XnaContentManagerRepro.Game1.LoadContent() in D:\Temp\XnaContentManagerRepro\XnaContentManagerRepro\XnaContentManagerRepro\Game1.cs:line 53
at Microsoft.Xna.Framework.Game.Initialize()
at XnaContentManagerRepro.Game1.Initialize() in D:\Temp\XnaContentManagerRepro\XnaContentManagerRepro\XnaContentManagerRepro\Game1.cs:line 39
at Microsoft.Xna.Framework.Game.RunGame(Boolean useBlockingRun)
at Microsoft.Xna.Framework.Game.Run()
at XnaContentManagerRepro.Program.Main(String[] args) in D:\Temp\XnaContentManagerRepro\XnaContentManagerRepro\XnaContentManagerRepro\Program.cs:line 15
InnerException:
Wenn ich in der Zeile, die die Textur lädt, einen Haltepunkt setze und dann das Element untersuche ContentTypeReaderManager.nameToReader
, sehe ich Folgendes:
Wie Sie sehen können, ReflectiveReader
wird tatsächlich a für den Texture2D
Typ zugeordnet. Dies stammt aus meiner TestEntity
Klasse (siehe die Einträge über dem im obigen Bild hervorgehobenen). Aber wenn Sie meine Modellklassen untersuchen, hat nichts, was hängt, LevelData
eine Instanz von TestEntity
oder sogar Entity
darin!
Wenn ich die TestEntityData
Klasse in diese ändere :
public sealed class TestEntityData : EntityData<TestEntityData, Entity>
{
}
Die Ausnahme tritt nicht mehr auf. Das liegt daran, dass TestEntity
es nie in Betracht gezogen wird, also auch nicht Texture2D
. Daher werden ReflectiveReader
die generischen Typparameter in meinen Modellklassen betrachtet und verfolgt! Ich kann nur annehmen, dass dies ein Fehler ist - es macht für mich überhaupt keinen Sinn, warum dies notwendig wäre.
Meine Modellklassen haben diese generischen Typparameter aus gutem Grund - sie machen meinen Modellcode viel einfacher. Bin ich hier fest? Ist meine einzige Option, meine Modelle so umzugestalten, dass sie niemals einen generischen Typparameter meiner Entitätstypen haben? Ich habe überlegt, zu verwenden ContentSerializerIgnoreAttribute
, aber das funktioniert nur gegen Eigenschaften und Felder, was sinnvoll ist, wenn man bedenkt, dass dies die einzigen Dinge sind, die die Serialisierung beeinflussen sollten.
Hat jemand einen Rat?
Load<Texture2D>
funktioniert, wenn der reflektierende Leser nicht zuerst dort angekommen ist und behauptet, dass er für das Laden von Texturen verantwortlich ist. Wenn ich zum Beispiel den Aufruf zum Laden meiner Teststufe überspringe, wird die Textur erfolgreich mit XNAs TextureReader
oder wie auch immer sie heißt geladen . Ich bestreite, dass die generischen Parameter einen Einfluss auf die Serialisierung haben. Die Serialisierung befasst sich nur mit dem Status eines Objekts, und das betreffende Objekt enthält keine Entität. Der generische Parameter wird nur in Methoden für das Objekt verwendet, nicht in Daten.
Type.GetGenericArguments
einen geschlossenen generischen Typ oder einen offenen generischen Typ abgerufen werden . Möglicherweise sind die Dokumente falsch und Sie haben Recht, aber die Dokumente erklären, warum Texture2D vom Reflection-System abgedeckt wird und daher in Ihrem Serialisierungscode angezeigt wird. Vielleicht könnten Sie bei MSDN nachfragen, da anscheinend hier niemand eine bessere Idee hat.
Load<Texture2D>
gelingen, ohne eine Ausnahme auszulösen ? Ihre Frage ist ziemlich klar, aber es ist nicht klar, wie sich Ihr Beispiel darauf bezieht. Ich würde jedoch sagen, dass bei der Serialisierung generische Typen berücksichtigt werden müssen, da sonst nicht garantiert werden kann, dass alles, was aus dem Stream gelesen wird, rekonstruiert werden kann.