C#,访问类中不同列表的通用方法

本文关键字:列表 方法 访问 | 更新日期: 2023-09-27 18:34:19

我有一个由 3 个不同的链表组成的类(用于保存我正在开发的游戏中的实体(。这些列表都是具有相同基本类型的对象,但出于处理原因,我将它们分开。请注意,IEntity、IObject 和 IUndead 都继承自 IEntity。

public class EntityBucket
{
    public LinkedList<IEntity> undeadEntities;
    public LinkedList<IEntity> objects;
    public LinkedList<IEntity> livingEntities;
    public EntityBucket()
    {
        undeadEntities = new LinkedList<IEntity>();
        objects = new LinkedList<IEntity>();
        livingEntities = new LinkedList<IEntity>();
    }
    public LinkedList<IEntity> GetList(IObject e)
    {
        return objects;
    }
    public LinkedList<IEntity> GetList(IUndead e)
    {
        return undeadEntities;
    }
    public LinkedList<IEntity> GetList(ILiving e)
    {
        return livingEntities;
    }
}

我有 3 种方法可以检索每个列表,目前基于它们的参数。有 3 个的事实很好,因为我知道每个列表都会以某种方式需要自己的访问器。但是,传递实例化的对象并不理想,因为我可能希望在手头没有类似类型的对象的情况下在某处检索列表。请注意,这里的对象甚至没有在 GetList 方法中使用,它们只是为了确定要使用的版本。下面是一个示例,其中我手头有一个实例化的对象:

public void Delete(IUndead e, World world)
{
     .....
     LinkedList<IEntity> list = buckets[k].GetList(e);
     .....
}

我不喜欢当前的实现,因为我可能并不总是手头有一个实例化的对象(例如,在渲染实体时(。我正在考虑一般地这样做,但我不确定我想做的事情是否可能。有了这个,我还需要 3 个删除方法(以及任何其他方法的 3 个,例如添加等( - 每种类型一个,IUndead、IObject 和 ILiving。我只是觉得这不是正确的做法。

我会根据要求发布到目前为止我尝试做的事情,但我的泛型相当糟糕,我觉得任何人阅读这篇文章也是一种浪费。

最后,性能非常重要。我不是过早优化,我正在后期优化,因为我已经有工作代码了,但需要它更快。getlist 方法将经常被调用,我想避免任何显式的类型检查。

C#,访问类中不同列表的通用方法

所以你想要一个更好的界面,因为,正如你所说,将一个不必要的对象传递给GetList只是为了弄清楚它的类型是没有意义的。

您可以执行以下操作:

public List<IEntity> GetList<T>() : where T:IEntity
{
    if(typeof(T)==typeof(IUndead)) return undedEntities;
    // and so on
}

你必须这样称呼它:GetList<IUndead>();

我认为枚举在这里是一个更好的主意:

enum EntityTypes { Undead, Alive, Object };
public List<IEntity> GetList(EntityTypes entityType) { ... }

它更干净,对我来说更有意义。

编辑:使用泛型实际上并不那么简单。有人可以将 GetList 称为实现 IUndead 的 Zombie 类型,然后您必须检查接口实现。有人甚至可以给你一个同时实现IUndead和IAlive的LiveZombie。一定要用枚举。

更好的

实现与更好的接口搭配怎么样?

public class EntityBucket
{
  public LinkedList<IEntity> Entities;
  public IEnumerable<T> GetEntities<T>() where T : IEntity
  {
    return Entities.OfType<T>();
  }
}

List<IUndead> myBrainFinders = bucket.GetEntities<IUndead>().ToList();

通过此实现,调用方最好将每个项目添加到正确的列表中。 这是您原始实现的要求,所以我认为这没有问题。

public class EntityBucket
{
  Dictionary<Type, List<IEntity>> entities = new Dictionary<Type, List<IEntity>>();
  public void Add<T>(T item) where T : IEntity
  {
    Type tType = typeof(T);
    if (!entities.ContainsKey(tType))
    {
      entities.Add(tType, new List<IEntity>());
    }
    entities[tType].Add(item);
  }
  public List<T> GetList<T>() where T : IEntity
  {
    Type tType = typeof(T);
    if (!entities.ContainsKey(tType))
    {
      return new List<T>();
    }
    return entities[tType].Cast<T>().ToList();
  }
  public List<IEntity> GetAll()
  {
    return entities.SelectMany(kvp => kvp.Value)
      .Distinct() //to remove items added multiple times, or to multiple lists
      .ToList();
  }
}

像下面这样的东西怎么样?

public LinkedList<IEntity> GetList(Type type) {
    if (typeof(IUndead).IsAssignableFrom(type)) return undeadEntities;
    if (typeof(ILiving).IsAssignableFrom(type)) return livingEntities;
    if (typeof(IObject).IsAssignableFrom(type)) return objects;
}

然后你会这样称呼它:

var myUndeads = GetList(typeof(IUndead));
var myLivings = GetList(typeof(ILiving));
// etc

可以在删除、添加和其他方法中实现相同类型的逻辑,并且永远不需要对象的具体实例来访问它们。

IsAssignableFrom逻辑可以很好地处理子类化(即你可以有一个CatZombie,它派生自Zombie,它实现了IUndead,这仍然有效(。这意味着您仍然只需要创建一个 Delete 方法,如下所示:

public void Delete(IEntity e, World world) {
    if (typeof(IUndead).IsAssignableFrom(type)) undeadEntities.Remove(e);
    if (typeof(ILiving).IsAssignableFrom(type)) livingEntities.Remove(e);
    if (typeof(IObject).IsAssignableFrom(type)) objects.Remove(e);
}

编辑:我注意到你对zmbq关于性能的答案的评论;这绝对不快。如果需要高性能,请使用枚举样式的方法。您的代码将更加冗长,需要更多的维护,但您将获得更好的性能。

在我看来,

你可以实现一个字典命名的链表并引用它们按名称或枚举。

这样,添加或删除列表只是一个实现问题,没有单独的类要处理。