Akhirnya saya sampai pada masalah dan saya bertanya-tanya apa jalan terbaik saya. Singkatnya, masalahnya adalah bahwa XNA ReflectiveReader
merefleksikan ke dalam parameter tipe generik, bahkan jika tidak ada instance dari tipe generik yang disimpan dalam objek yang serial.
Contoh terbaik menunjukkan ini. Pertimbangkan kelas model berikut:
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;
}
}
}
Sekarang anggaplah saya ingin mendefinisikan instance LevelData di dalam file XML untuk kemudian dimuat dengan 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>
Sekarang pertimbangkan logika muat sederhana ini:
Content.Load<LevelData>("Test");
Content.Load<Texture2D>("Texture");
Baris pertama berhasil, tetapi yang kedua melempar pengecualian:
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:
Jika saya menetapkan breakpoint pada garis yang memuat tekstur dan kemudian memeriksa ContentTypeReaderManager.nameToReader
anggota, saya melihat ini:
Seperti yang Anda lihat, a ReflectiveReader
memang sedang dipetakan untuk Texture2D
jenisnya. Ini berasal dari TestEntity
kelas saya (lihat entri di atas yang disorot pada gambar di atas). Tetapi jika Anda memeriksa kelas model saya, tidak ada yang menggantung yang LevelData
memiliki instance TestEntity
atau bahkan Entity
di dalamnya!
Jika saya mengubah TestEntityData
kelas menjadi ini:
public sealed class TestEntityData : EntityData<TestEntityData, Entity>
{
}
Pengecualian tidak lagi terjadi. Itu karena TestEntity
tidak pernah dipertimbangkan, begitu juga tidak Texture2D
. Jadi, ReflectiveReader
sedang melihat - dan mengikuti - parameter tipe generik di kelas model saya! Saya hanya dapat berasumsi bahwa ini adalah bug - tidak masuk akal sama sekali bagi saya mengapa ini perlu.
Kelas model saya memiliki parameter tipe umum ini untuk alasan yang baik - mereka membuat kode model saya lebih sederhana. Apakah saya terjebak di sini? Apakah satu-satunya pilihan saya untuk memperbaiki model saya agar tidak pernah memiliki parameter tipe generik dari tipe entitas saya? Saya mempertimbangkan untuk menggunakan ContentSerializerIgnoreAttribute
, tetapi itu hanya bekerja terhadap properti dan bidang, yang masuk akal mengingat mereka satu-satunya hal yang harus memengaruhi serialisasi.
Adakah yang punya saran?
Load<Texture2D>
berhasil tanpa menaikkan pengecualian? Pertanyaan Anda cukup jelas tetapi tidak jelas bagaimana contoh Anda terkait dengan itu. Namun saya akan mengatakan bahwa serialisasi memang harus melihat tipe generik, karena kalau tidak serialisasi tidak dapat dijamin dapat merekonstruksi apa pun yang dibaca dari stream.Load<Texture2D>
berfungsi jika pembaca reflektif belum masuk ke sana terlebih dahulu dan mengklaim bahwa itu bertanggung jawab untuk memuat tekstur. Jika, misalnya, saya melewatkan panggilan untuk memuat tingkat pengujian saya maka tekstur berhasil dimuat menggunakan XNATextureReader
atau apa pun namanya. Saya membantah bahwa parameter generik ada hubungannya dengan serialisasi. Serialisasi hanya berkaitan dengan keadaan suatu objek, dan objek yang dimaksud tidak memiliki entitas di dalamnya. Parameter generik hanya digunakan dalam metode pada objek, bukan pada data.Type.GetGenericArguments
, apakah tipe generik tertutup atau tipe generik terbuka. Mungkin dokumen salah dan Anda benar, tetapi dokumen menjelaskan mengapa Texture2D dicakup oleh sistem Refleksi dan karenanya muncul dalam kode serialisasi Anda. Mungkin Anda bisa bertanya pada MSDN karena sepertinya tidak ada orang di sini yang memiliki ide yang lebih baik.Jawaban:
Meskipun memang benar bahwa secara umum , serialisasi tidak harus berkaitan dengan jenis objek yang dipertanyakan dan hanya mencatat representasi negara mereka ... tidak semua implementasi serialisasi melakukan itu. Sebagian besar built-in NET metode serialisasi lakukan mencatat informasi tentang jenis berpartisipasi dalam serialisasi. Ada keuntungan untuk pilihan itu (memungkinkan untuk validasi yang lebih kuat) serta kerugian (ukuran objek serial yang lebih besar), tetapi itu tidak salah per se dan Anda hanya harus hidup dengannya.
Pipeline konten XNA, untuk tipe Anda, melintasi grafik properti serializable (dan bidang) dan membuat pembaca untuknya. Anda dapat melihat perilaku ini jika Anda memeriksa inisialisasi untuk
ReflectiveReader<T>
(Initialize
metode, bukan konstruktor). Ini dilakukan melalui refleksi, bukan berdasarkan data aktual dalam XML (sekali lagi, ini dapat diverifikasi dengan melihat kode yang direfleksikan). Jadi tidak masalah jika ada referensi ke tekstur dalam data Anda atau tidak, jika adaTexture2D
properti dalam grafik properti tipe itu, itu akan mencoba untuk membuat pembaca untuk itu sebagai bagian dari inisialisasi pipa konten.Anda tidak seharusnya menggunakan referensi langsung ke
Texture2D
objek dalam konten khusus Anda. Anda mungkin menemukan utas ini (atau yang ini , sedikit banyak). Solusi yang diduga untuk masalah ini adalah menggunakan referensi eksternalTexture2DContent
sebagai gantinya.sumber