Finalmente he llegado al fondo de un problema y me pregunto cuál es mi mejor recurso. En resumen, el problema es que los XNA se ReflectiveReader
reflejan en parámetros de tipo genérico, incluso si no se almacena ninguna instancia de ese tipo genérico en el objeto que se está serializando.
Un ejemplo lo demuestra mejor. Considere las siguientes clases de modelo:
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;
}
}
}
Ahora suponga que quiero definir una instancia de LevelData dentro de un archivo XML para luego cargarla con 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>
Ahora considere esta lógica de carga simple:
Content.Load<LevelData>("Test");
Content.Load<Texture2D>("Texture");
La primera línea tiene éxito, pero la segunda arroja una excepción:
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 establezco un punto de interrupción en la línea que carga la textura y luego examino el ContentTypeReaderManager.nameToReader
miembro, veo esto:
Como puede ver, ReflectiveReader
se está mapeando a para el Texture2D
tipo. Esto se deriva de mi TestEntity
clase (ver las entradas arriba de la destacada en la imagen de arriba). Pero si examinas mis clases de modelos, ¡nada que cuelgue LevelData
tiene una instancia TestEntity
o incluso Entity
en ella!
Si cambio la TestEntityData
clase a esto:
public sealed class TestEntityData : EntityData<TestEntityData, Entity>
{
}
La excepción ya no ocurre. Eso es porque TestEntity
nunca se considera, por lo que tampoco lo es Texture2D
. ¡Por lo tanto, ReflectiveReader
está mirando, y siguiendo, los parámetros de tipo genérico en mis clases de modelo! Solo puedo suponer que esto es un error, no tiene ningún sentido para mí por qué esto sería necesario.
Mis clases de modelo tienen estos parámetros de tipo genérico por una buena razón: hacen que el código de mi modelo sea mucho más simple. ¿Estoy atrapado aquí? ¿Es mi única opción refactorizar mis modelos para que nunca tengan un parámetro de tipo genérico de mis tipos de entidad? Pensé en usar ContentSerializerIgnoreAttribute
, pero eso solo funciona contra propiedades y campos, lo que tiene sentido teniendo en cuenta que son las únicas cosas que deberían influir en la serialización.
Alguien tiene algún consejo?
Load<Texture2D>
tener éxito sin generar una excepción? Su pregunta es bastante clara, pero no está claro cómo se relaciona su ejemplo con ella. Sin embargo, diría que la serialización tiene que buscar tipos genéricos, porque de lo contrario no se puede garantizar que pueda reconstruir lo que lea de la transmisión.Load<Texture2D>
funcionan si el lector reflexivo no ha entrado allí primero y afirma que es responsable de cargar texturas. Si, por ejemplo, me salto la llamada para cargar mi nivel de prueba, la textura se carga con éxito usando XNATextureReader
o como se llame. Discuto que los parámetros genéricos tengan alguna relación con la serialización. La serialización se refiere solo al estado de un objeto, y el objeto en cuestión no tiene entidad. El parámetro genérico solo se usa en métodos en el objeto, no en datos.Type.GetGenericArguments
, ya sea un tipo genérico cerrado o un tipo genérico abierto. Tal vez los documentos están equivocados y usted tiene razón, pero los documentos explican por qué Texture2D está cubierto por el sistema Reflection y, por lo tanto, aparecen en su código de serialización. Tal vez podría preguntar en MSDN ya que no parece que nadie aquí tenga una mejor idea.Respuestas:
Si bien es cierto que, en general , la serialización no necesariamente tiene que preocuparse por los tipos de los objetos en cuestión y solo registrar las representaciones de su estado ... no todas las implementaciones de serialización hacen eso. La mayoría de los métodos de serialización incorporado en .NET hacer registran información sobre los tipos de participantes en la serialización. Hay ventajas en esa elección (que permite una validación más sólida), así como desventajas (mayor tamaño de objeto serializado), pero no está mal per se y solo tienes que vivir con él.
La canalización de contenido de XNA, para sus tipos, atraviesa el gráfico de propiedades serializables (y de campo) y crea lectores para ellos. Puede ver este comportamiento si examina la inicialización de
ReflectiveReader<T>
(elInitialize
método, no el constructor). Lo hace a través de la reflexión, no se basa en los datos reales en el XML (de nuevo, esto se puede verificar observando el código reflejado). Por lo tanto, no importa si hay una referencia a la textura en sus datos o no, si hay unaTexture2D
propiedad en el gráfico de propiedades del tipo, intentará crear un lector para ello como parte de la inicialización de la canalización de contenido.Se supone que no debe utilizar referencias directas a
Texture2D
objetos en su contenido personalizado. Puede encontrar este hilo (o este , en menor grado). La supuesta solución al problema es utilizar referencias externas en suTexture2DContent
lugar.fuente