Je suis enfin allé au fond d'un problème et je me demande quel est mon meilleur recours. En bref, le problème est que les XNA se ReflectiveReader
reflètent dans les paramètres de type générique, même si aucune instance de ce type générique n'est stockée dans l'objet en cours de sérialisation.
Un exemple le démontre le mieux. Considérez les classes de modèle suivantes:
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;
}
}
}
Supposons maintenant que je souhaite définir une instance de LevelData dans un fichier XML à charger ultérieurement avec ContentManager
( Test.xml ):
<?xml version="1.0" encoding="utf-8"?>
<XnaContent xmlns:Model="Model">
<Asset Type="Model:LevelData">
<Entities>
<Item Type="Model:TestEntityData">
</Item>
</Entities>
</Asset>
</XnaContent>
Considérez maintenant cette logique de chargement simple:
Content.Load<LevelData>("Test");
Content.Load<Texture2D>("Texture");
La première ligne réussit, mais la seconde lève une exception:
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:
Si je mets un point d'arrêt sur la ligne qui charge la texture puis examine le ContentTypeReaderManager.nameToReader
membre, je vois ceci:
Comme vous pouvez le voir, a ReflectiveReader
est en effet mappé pour le Texture2D
type. Cela vient de ma TestEntity
classe (voir les entrées au-dessus de celle mise en évidence dans l'image ci-dessus). Mais si vous examinez mes classes de modèles, rien de suspendu LevelData
n'a une instance TestEntity
ou même Entity
dedans!
Si je change la TestEntityData
classe en ceci:
public sealed class TestEntityData : EntityData<TestEntityData, Entity>
{
}
L'exception ne se produit plus. C'est parce que TestEntity
n'est jamais considéré, donc non plus Texture2D
. Ainsi, le ReflectiveReader
regarde - et suit - les paramètres de type génériques dans mes classes de modèles! Je peux seulement supposer que c'est un bug - cela n'a aucun sens pour moi pourquoi cela serait nécessaire.
Mes classes de modèles ont ces paramètres de type génériques pour une bonne raison - elles rendent mon code de modèle beaucoup plus simple. Suis-je coincé ici? Est-ce que ma seule option est de refactoriser mes modèles pour ne jamais avoir de paramètre de type générique de mes types d'entité? J'ai envisagé d'utiliser ContentSerializerIgnoreAttribute
, mais cela ne fonctionne que contre les propriétés et les champs, ce qui est logique étant donné que ce sont les seules choses qui devraient influencer la sérialisation.
Quelqu'un a-t-il des conseils?
Load<Texture2D>
fonctionne si le lecteur réfléchissant n'y est pas entré en premier et prétend qu'il est responsable du chargement des textures. Si, par exemple, je saute l'appel pour charger mon niveau de test, la texture se charge avec succès à l'aide de XNA TextureReader
ou de tout autre nom. Je conteste que les paramètres génériques aient une incidence sur la sérialisation. La sérialisation ne concerne que l'état d'un objet, et l'objet en question n'a pas d'entité en lui. Le paramètre générique n'est utilisé que dans les méthodes sur l'objet, pas dans les données.
Type.GetGenericArguments
, qu'il s'agisse d'un type générique fermé ou d'un type générique ouvert. Peut-être que les documents sont faux et que vous avez raison, mais les documents expliquent pourquoi Texture2D est couvert par le système Reflection et apparaît donc dans votre code de sérialisation. Peut-être pourriez-vous demander sur MSDN car il ne semble pas que quiconque ici ait une meilleure idée.
Load<Texture2D>
réussir sans déclencher une exception? Votre question est assez claire mais il n'est pas clair comment votre exemple s'y rapporte. Je dirais cependant que la sérialisation doit se pencher sur les types génériques, car sinon, il ne peut pas être garanti de pouvoir reconstruire tout ce qu'elle lit dans le flux.