Я наконец-то понял причину проблемы и мне интересно, каково мое лучшее решение. Короче говоря, проблема в том, что XNA ReflectiveReader
отражается в параметрах универсального типа, даже если ни один экземпляр этого универсального типа не сохраняется в сериализуемом объекте.
Пример лучше всего демонстрирует это. Рассмотрим следующие модельные классы:
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;
}
}
}
Теперь предположим, что я хочу определить экземпляр LevelData внутри XML-файла для последующей загрузки с помощью 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>
Теперь рассмотрим эту простую логику загрузки:
Content.Load<LevelData>("Test");
Content.Load<Texture2D>("Texture");
Первая строка завершается успешно, но вторая создает исключение:
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:
Если я установлю точку останова на строке, которая загружает текстуру, а затем проверим ContentTypeReaderManager.nameToReader
элемент, я вижу это:
Как вы можете видеть, ReflectiveReader
для Texture2D
типа действительно отображается a . Это связано с моим TestEntity
классом (см. Записи выше той, что выделена на изображении выше). Но если вы изучите мои классы моделей, ничто не висит или даже LevelData
не содержит их!TestEntity
Entity
Если я изменю TestEntityData
класс на это:
public sealed class TestEntityData : EntityData<TestEntityData, Entity>
{
}
Исключение больше не возникает. Это потому, что TestEntity
никогда не рассматривается, так что нет Texture2D
. Таким образом, ReflectiveReader
мы рассматриваем и следуем за параметрами универсального типа в моих модельных классах! Я могу только предположить, что это ошибка - для меня нет никакого смысла, зачем это нужно.
Мои классы моделей имеют эти параметры универсального типа по уважительной причине - они значительно упрощают код моей модели. Я застрял здесь? Является ли мой единственный вариант реорганизации моих моделей, чтобы никогда не иметь универсального параметра типа моих типов сущностей? Я подумал об использовании ContentSerializerIgnoreAttribute
, но это работает только против свойств и полей, что имеет смысл, учитывая, что это единственное, что должно влиять на сериализацию.
У кого-нибудь есть совет?
Load<Texture2D>
добиться успеха, не вызывая исключения? Ваш вопрос довольно ясен, но не ясно, как ваш пример относится к нему. Однако я бы сказал, что сериализация должна учитывать общие типы, потому что в противном случае нельзя гарантировать, что она сможет восстановить все, что читает из потока.Load<Texture2D>
работает, если рефлексивный ридер не попал туда первым и заявил, что он отвечает за загрузку текстур. Если, например, я пропущу вызов, чтобы загрузить свой тестовый уровень, то текстура успешно загрузится с использованием XNATextureReader
или как там это называется. Я спорю, что общие параметры имеют какое-либо отношение к сериализации. Сериализация касается только состояния объекта, и у рассматриваемого объекта нет сущности в нем. Универсальный параметр используется только в методах объекта, а не в данных.Type.GetGenericArguments
закрытого универсального типа или открытого универсального типа. Возможно, документы не правы, а вы правы, но они объясняют, почему Texture2D покрывается системой Reflection, и, следовательно, отображаются в вашем коде сериализации. Может быть, вы могли бы спросить на MSDN, так как, кажется, ни у кого здесь нет лучшей идеи.Ответы:
Хотя это правда , что в целом , сериализация не обязательно самая озабоченность с типами объектов , о которых идет речь , и только рекордное представление их состояний ... не все реализации сериализации сделать это. Большинство встроенных в .NET методов сериализации сделать запись информации о типах участвующих в сериализации. У этого выбора есть свои преимущества (допускающие более надежную проверку), а также недостатки (больший размер сериализованного объекта), но это не так само по себе, и вам просто нужно с этим жить.
Конвейер содержимого XNA для ваших типов пересекает граф сериализуемых свойств (и полей) и создает для них читатели. Вы можете увидеть это поведение, если изучите инициализацию для
ReflectiveReader<T>
(Initialize
метод, а не конструктор). Это делается с помощью отражения, а не на основе фактических данных в XML (опять же, это можно проверить, посмотрев на отраженный код). Таким образом, не имеет значения, есть ли ссылка на текстуру в ваших данных или нет, если естьTexture2D
свойство в графе свойств типа, оно попытается создать для него читатель как часть инициализации конвейера содержимого.Вы не должны использовать прямые ссылки на
Texture2D
объекты в вашем пользовательском контенте. Вы можете найти эту ветку (или эту , в меньшей степени). Предполагаемое решение проблемы заключается в использовании внешних ссылок наTexture2DContent
.источник