这篇文章包含很多代码,但是如果你花一些时间阅读并理解它,我会非常感激...并希望能找到一个解决方案
让我们假设我正在构建一个网络游戏,其中需要绘制实体,并相应地从服务器更新其中一些实体。
Drawable
班负责在屏幕上绘制实体:
class Drawable
{
public int ID { get; set; } // I put the ID here instead of having another
// class that Drawable derives from, as to not
// further complicate the example code.
public void Draw() { }
}
从服务器接收的数据实现IData
,每个具体IData
持有不同的属性。假设我们有Player
和Enemy
将收到的以下数据:
interface IData
{
int ID { get; set; }
}
class PlayerData : IData
{
public int ID { get; set; }
public string Name { get; set; }
}
class EnemyData : IData
{
public int ID { get; set; }
public int Damage { get; set; }
}
应该可以从服务器实现IUpdateable<T>
更新的游戏实体,其中T
为IData
:
interface IUpdateable<T> where T : IData
{
void Update(T data);
}
因此,我们的实体为Drawable
和Updateable
:
class Enemy : Drawable, IUpdateable<EnemyData>
{
public void Update(EnemyData data) { }
}
class Player : Drawable, IUpdateable<PlayerData>
{
public void Update(PlayerData data) {}
}
这就是游戏的基本结构。
现在,我需要存储这些Dictionary
和Drawable
个Updateable
个对象,将Drawable
的ID存储为密钥,并保存一个复杂的对象。 Drawable
和Updateable
个对象及其远程具体IData
:
class DataHolder<T, T1> where T:Drawable, IUpdateable<T1> where T1:IData
{
public T Entity{ get; set;}
public IData Data{ get; set;}
public DataHolder(T entity, IData data)
{
Entity = entity;
Data = data;
}
}
举个例子,假设我目前有以下实体:
var p1 = new Player();
var p1Data = new PlayerData();
var e1 = new Enemy();
var e1Data = new EnemyData();
var playerData = new DataHolder<Player, PlayerData>(p1, p1Data);
var enemyData = new DataHolder<Enemy, EnemyData>(e1, e1Data);
我现在需要Dictionary
将实体的ID作为密钥(p1.ID
和e1.ID
)及其DataHolder
(playerData
和enemyData
)及其价值。
如下所示(以下代码只显示了我想要做的事情,因此它不会编译):
Dictionary<int, DataHolder> list = new Dictionary<int, DataHolder>();
list.Add(p1.ID, playerData);
list.Add(e1.ID, enemyData);
如何构建这样的词典?
[更新]
关于使用情况,我需要能够执行以下操作:
foreach (var value in list.Values)
{
var entity = value.Entity;
entity.Update(value.Data);
}
我还尝试将DataHolder
的设计更改为以下内容:
class DataHolder<T> where T:Drawable, IUpdateable<IData>
{
public T Entity{ get; set;}
public IData Data{ get; set;}
public DataHolder(T entity, IData data)
{
Entity = entity;
Data = data;
}
}
然后我尝试了以下内容:
var playerData = new DataHolder<Player>(p1, p1Data); //error
但是这会引发编译时错误:
“玩家”类型必须是可转换的 'IUpdateable&lt; IData&gt;'为了 将它用作参数'T' 泛型类'DataHolder&lt; T&gt;'
这是什么原因引起的? Player
实施IUpdateable<PlayerData>
和PlayerData
实施IData
。这是一个有差异的问题吗?它有什么办法吗?
答案 0 :(得分:2)
使您的DataHolder
类实现或继承非泛型类或接口,然后创建该(非泛型)类型的字典。
答案 1 :(得分:1)
你写这篇文章的方式,没有理由不这样声明你的字典:
Dictionary<int, object> list = new Dictionary<int, object>();
除了构造函数之外, DataHolder
没有公共方法/属性,因此在构造实例后它是无用的类型。它并不比object
如果您打算对它们进行公共操作并且它们的参数和返回值不需要模板化值,那么您可以从DataHolder
中提取非模板化接口并使其成为字典的值类型
答案 2 :(得分:1)
我认为你可能需要先创建一个新类。
class DrawableUpdatable<T> : Drawable, IUpdatable<T> {
public void Update() { base.Update(); }
}
让Player
和Enemy
从中派生出来,并且您的DataHolder将其用作第一个通用约束。这将允许您定义新的DataHolder<DrawableUpdatable<IData>, IData>
。否则你将无法创建一个,因为Enemy
和Player
都没有单个对象。 (你不能说DataHolder<Drawable **and** IUpdatable<IData>, IData>
我认为你的设计无论如何都要过于复杂 - 它会吓跑任何来看它并维护它的新开发者。在使用更复杂的代码修复它之前,我会考虑修改它。
答案 3 :(得分:1)
请仔细阅读所有代码。我想这会得到你想要的东西。
首先,您需要一个IDrawable
界面
interface IDrawable { int ID { get; set; } }
与显而易见的class Drawable : IDrawable { .. }
一起
那么你需要一个IEntity
接口
interface IEntity : IDrawable, IUpdatetable<IData> { }
与实施一起
class Enemy : Drawable, IEntity
{
public Enemy(int id) : base(id) { }
public void Update(EnemyData data)
{ ... }
void IUpdatetable<IData>.Update(IData data)
{
Update(data as EnemyData);
}
}
class Player : Drawable, IEntity
{
public Player(int id) : base(id) { }
public void Update(PlayerData data)
{ ... }
void IUpdatetable<IData>.Update(IData data)
{
Update(data as PlayerData);
}
}
然后你需要统一DataHolder
interface IDataHolder
{
IEntity Entity { get; set; }
IData Data { get; set; }
}
class DataHolder<T,T1> : IDataHolder
where T : class, IEntity
where T1 : class, IData
{
T entity;
T1 data;
public DataHolder(T entity, T1 data)
{
this.entity = entity;
this.data = data;
}
public T Entity { get { return entity; } set { entity = value; } }
public T1 Data { get { return data; } set { data = value; } }
IEntity IDataHolder.Entity
{
get { return entity; }
set { entity = value as T; }
}
IData IDataHolder.Data
{
get { return data; }
set { data = value as T1; }
}
}
最后使用KeyedCollection来保存所有内容并公开keys属性
public class DataBag : KeyedCollection<int, IDataHolder>
{
protected override int GetKeyForItem(IDataHolder item)
{
return item.Entity.ID;
}
public ICollection<int> Keys
{
get { return Dictionary.Keys; }
}
}
这是测试代码:
public void TestCode()
{
Player p1 = new Player(100);
Enemy e1 = new Enemy(1);
PlayerData p1data = new PlayerData(11, "joe");
EnemyData e1data = new EnemyData(12, 1000);
DataHolder<Player, PlayerData> bag1
= new DataHolder<Player, PlayerData>(p1, p1data);
DataHolder<Enemy, EnemyData> bag2
= new DataHolder<Enemy, EnemyData>(e1, e1data);
Dictionary<int, IDataHolder> list = new Dictionary<int, IDataHolder>();
list.Add(p1.ID, bag1);
list.Add(e1.ID, bag2);
foreach (int id in list.Keys )
{
IDataHolder item = list[id];
// you can do this here:
// item.Entity.Update(item.Data);
// or get the type specific version below:
if (item.Entity is Player)
{
Player player = item.Entity as Player;
PlayerData pdata = item.Data as PlayerData;
player.Update(pdata);
Console.WriteLine("ID={0} PlayerName={1} DataId={2}",
player.ID, pdata.Name, pdata.ID);
}
else if (item.Entity is Enemy)
{
Enemy enemy = item.Entity as Enemy;
EnemyData edata = item.Data as EnemyData;
enemy.Update(edata);
Console.WriteLine("ID={0} EnemyDamage={1} DataId={2}",
enemy.ID, edata.Damage, edata.ID);
}
}
}
它以微笑编译并产生输出
ID=100 PlayerName=joe DataId=11
ID=1 EnemyDamage=1000 DataId=12