XNAのContentManagerがシリアル化のためにジェネリック型パラメーターに従うのはなぜですか?


8

ようやく問題の根底に達し、私の最善の解決策は何なのかと思っています。つまり、ReflectiveReaderシリアル化されているオブジェクトにジェネリック型のインスタンスが格納されていなくても、XNA がジェネリック型パラメーターに反映されるという問題があります。

例がこれを最もよく示しています。次のモデルクラスを検討してください。

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;
        }
    }
}

次に、後でContentManagerTest.xml)を使用してロードするために、XMLファイル内にLevelDataのインスタンスを定義するとします。

<?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");

最初の行は成功しますが、2番目の行は例外をスローします。

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メンバーを調べると、次のようになります。

ここに画像の説明を入力してください

ご覧のとおり、a ReflectiveReaderは実際にTexture2D型にマップされています。これは私のTestEntityクラスに由来します(上の画像で強調表示されているものの上のエントリを参照してください)。しかし、私のモデルクラスを調べると、ハングアップしLevelDataているインスタンスがTestEntity、そのEntity中に、またはその中にさえありません。

TestEntityDataクラスをこれに変更すると:

public sealed class TestEntityData : EntityData<TestEntityData, Entity>
{
}

例外は発生しなくなりました。これTestEntityは考慮されないため、どちらも考慮されませんTexture2D。したがって、はReflectiveReader私のモデルクラスのジェネリック型パラメーターを調べており、それに続いています。私はこれがバグであるとしか想定できません-なぜこれが必要になるのか私にはまったく意味がありません。

私のモデルクラスには、これらのジェネリック型パラメーターがあるのには十分な理由があります。これにより、モデルコードがはるかに単純になります。私はここで行き詰まっていますか?モデルをリファクタリングしてエンティティタイプのジェネリックタイプパラメータを持たないようにする唯一のオプションはありますか?私はの使用を検討しましたContentSerializerIgnoreAttributeが、それはプロパティとフィールドに対してのみ機能するため、シリアル化に影響を与える必要があるのはそれらだけであることを考えると理にかなっています。

誰かアドバイスはありますか?


私はXNAに詳しくありませんが、Texture2Dを考慮から外した場合Load<Texture2D>、例外を発生させずに成功するにはどうすればよいですか?あなたの質問はかなり明確ですが、あなたの例がそれとどのように関連しているかは明確ではありません。ただし、シリアル化ではジェネリック型を確認する必要があると言います。そうしないと、ストリームから読み取ったものを再構築できることが保証されないためです。
カイロタン

Load<Texture2D>反射型リーダーが最初にそこにいなくて、テクスチャをロードする責任があると主張した場合、呼び出しは機能します。たとえば、テストレベルを読み込むための呼び出しをスキップすると、XNA TextureReaderまたは呼び出されたものを使用してテクスチャが正常に読み込まれます。汎用パラメーターがシリアル化に何らかの影響を与えることに異議を唱えます。シリアル化はオブジェクトの状態のみに関係し、問題のオブジェクトにはエンティティがありません。ジェネリックパラメーターは、データではなく、オブジェクトのメソッドでのみ使用されます。

@ user13414、シリアライゼーションは、反対側でオブジェクトを再作成するために、それがどのような種類のオブジェクトであるかを正確に知る必要があります。たとえば、呼び出すコンストラクターがあります。オブジェクトのタイプには、少なくともC#やC ++などの言語ではジェネリックパラメーターとして渡される特定の引数が含まれます(ジェネリックを多少異なる方法で実装するJavaではない可能性があります)。
カイロタン2012年

@Kylotan:基本クラスはジェネリックであり、サブクラス(シリアル化されるオブジェクト)ではありません。オープンジェネリック型ではなく、クローズジェネリック型です。

2
私がリンクしたドキュメントには、.NETリフレクションがそれらの型パラメーターに関するジェネリック型に関する情報を格納することが記載されています。これはType.GetGenericArguments、閉じたジェネリック型かオープンなジェネリック型かにかかわらず、を介して取得できます。おそらくドキュメントは間違っていて、あなたは正しいのですが、ドキュメントは、Texture2DがReflectionシステムでカバーされているため、シリアル化コードに表示される理由を説明しています。ここで誰もより良いアイデアを持っているようには見えないので、MSDNで質問することもできます。
カイロタン2012年

回答:


4

確かに、一般的に、シリアライゼーションは必ずしも問題のオブジェクトのタイプに関係する必要はなく、状態の表現のみを記録する必要があります。すべてのシリアライゼーション実装がそうするわけではありません。.NET内蔵のシリアル化の方法のほとんどは行うシリアライゼーションに参加するタイプについての情報を記録します。その選択には利点があります(より堅牢な検証を可能にする)と同時に、欠点(シリアル化されたオブジェクトのサイズが大きくなる)もありますが、それ自体は問題ではなく、そのまま使用する必要があります。

XNAのコンテンツパイプラインは、タイプに応じて、シリアル化可能なプロパティ(およびフィールド)グラフを走査し、それらのリーダーを作成します。ReflectiveReader<T>Initializeコンストラクタではなくメソッド)の初期化を調べると、この動作を確認できます。これは、XMLの実際のデータに基づいてではなく、リフレクションを介して行われます(これも、リフレクトされたコードを確認することで確認できます)。したがって、データにテクスチャへの参照があるかどうかは関係ありません。Texture2D型のプロパティグラフにプロパティがある場合、コンテンツパイプラインの初期化の一部としてそのリーダーを作成しようとします。

Texture2Dカスタムコンテンツのオブジェクトへの直接参照を使用することは想定されていません。あなたは見つけることが、このスレッド(またはこれより少ない程度にし、)。問題の申し立てられた解決策は、Texture2DContent代わりに外部参照を使用することです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.