代码之家  ›  专栏  ›  技术社区  ›  Andreas Grech

C中涉及集合、泛型和接口的设计问题#

  •  2
  • Andreas Grech  · 技术社区  · 14 年前

    这篇文章包含了大量的代码,但是如果你花一些时间来阅读和理解它,我会非常感激的……希望能找到一个解决方案。

    假设我正在构建一个网络游戏,其中需要绘制实体,其中一些实体从服务器中相应地更新。

    这个 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 每种混凝土 伊达塔 持有不同的财产。假设我们有以下数据 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 伊达塔 :

    interface IUpdateable<T> where T : IData
    {
        void Update(T data);
    }
    

    因此,我们的实体 可拉伸的 Updateable :

    class Enemy : Drawable, IUpdateable<EnemyData>
    {
        public void Update(EnemyData data) { }
    }
    
    class Player : Drawable, IUpdateable<PlayerData>
    {
        public void Update(PlayerData data) {}
    }
    

    这就是游戏的基本结构。


    现在,我需要存储一个 Dictionary 这些 可拉伸的 可更新 对象,存储 可拉伸的 的ID作为键和保存 可拉伸的 可更新 物体及其遥控混凝土 伊达塔 :

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

    我现在需要一个 词典 将实体的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);
     }
    

    我也尝试改变 数据保持器 致:

    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
    

    但这会引发编译时错误:

    类型“player”必须是可转换的 按顺序到“i可更新<idata>” 将其用作 通用类“dataholder<t>”

    这是什么原因造成的? 玩家 器具 IUpdateable<PlayerData> PlayerData 器具 伊达塔 . 这是一个有差异的问题吗?有什么办法可以解决吗?

    4 回复  |  直到 14 年前
        1
  •  2
  •   SLaks    14 年前

    制作你的 DataHolder 类实现或继承非泛型类或接口,然后生成该(非泛型)类型的字典。

        2
  •  1
  •   David Gladfelter    14 年前

    你写这本书的时候,没有理由不把你的字典声明为:

    Dictionary<int, object> list = new Dictionary<int, object>();
    

    DataHolder 除了构造函数之外没有其他公共方法/属性,因此在构造实例之后,它是一个无用的类型。这不比 object

    如果您确实打算对它们进行公共操作,并且它们的参数和返回值不需要模板化的值,那么您可以从 数据保持器 并使之成为字典的值类型。

        3
  •  1
  •   Mark H    14 年前

    我想你可能需要先创建一个新的类。

    class DrawableUpdatable<T> : Drawable, IUpdatable<T> {
        public void Update() { base.Update(); }
    }
    

    有你 Player Enemy 从中派生,并且您的数据持有者将它用作第一个通用约束。这将允许您定义新的 DataHolder<DrawableUpdatable<IData>, IData> . 因为没有一个对象可以同时创建 敌人 玩家 得到。(你不能说 DataHolder<Drawable **and** IUpdatable<IData>, IData>

    不管怎样,我认为你的设计从一开始就太过复杂了——它会吓跑任何一个前来查看和维护它的新开发人员。在用更复杂的代码修复它之前,我会考虑修改它。

        4
  •  1
  •   John Alexiou    14 年前

    请仔细阅读所有代码。我想这会得到你想要的。 首先你需要一个 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