C# 按接口在多个集合中引用同一对象

C# 按接口在多个集合中引用同一对象,c#,interface,C#,Interface,警告:此问题以RPG为例。 比方说,我正在用C#制作我梦寐以求的RPG。 当玩家进入战斗时,会出现某种类型的战场,其中包含与战斗相关的所有元素的引用,如战场上可用的各种对手和物品 var IActors = (from list in battlefieldobjects where list is IActor select list).ToList(); 现在,所有这些元素都有一个但有几个角色: 例如,盟友(通过直接继承成为战斗机)能够在战场上移动、发布命令或成为恩尼米人的目标 现在,石头

警告:此问题以RPG为例。

比方说,我正在用C#制作我梦寐以求的RPG。 当玩家进入战斗时,会出现某种类型的战场,其中包含与战斗相关的所有元素的引用,如战场上可用的各种对手和物品

var IActors = (from list in battlefieldobjects where list is IActor select list).ToList();
现在,所有这些元素都有一个但有几个角色: 例如,盟友(通过直接继承成为战斗机)能够在战场上移动、发布命令或成为恩尼米人的目标

现在,石头中那把强大的剑也在战斗中扮演了一些角色。显然,它不能移动也不能发出命令,但它仍然可以成为目标,并且(希望)可以被提升或使用

在我的代码中,所有这些行为都由接口表示,因此可以使用具有相同行为的所有对象,而不管实现它的对象是什么

代码示例:

public class Ally : Fighter, IMovable, IActor, ITarget
{
    // IMovable implementation
    public void Move(...) { ... }

    // IActor implementation
    public bool HasInitiative { get { ... } }

    public ICommand IssueCommand(...) { ... }

    // ITarget implementation
    public void OnTarget(...) { ... }

    // other code ...
}

public class MightySword : ITarget, ILiftable, IUsable
{
    // ITarget implementation
    public void OnTarget(...) { ... }

    // ... other interface implementation

    // other code ...
}
现在我的问题是: 我的战地类应该如何保存对所有这些对象的引用? 我提出了两个可能的解决方案,但目前还不清楚哪一个是最好的,以及是否能找到更好的解决方案

解决方案A:

多次引用,直接访问: 使用此解决方案,每个对象在其实现的每个接口上引用一次,因此我的Battledget类如下所示:

public class Battlefield : ...
{
   IList<IMovable> movables;
   IList<ITarget> targets;
   IList<IActor> actors;
   // ... and so on
}
该解决方案允许我们直接访问对象的各种行为,但它引入了冗余,因为盟友必须被引用到动产、目标和参与者中 . 这种冗余意味着更大的内存占用,并将使在战场上添加和删除对象变得更加复杂(每种类型现在都应该从哪些集合中添加/删除对象)

另外,添加一个新接口意味着添加一个新集合来保存项目并管理相应的代码

解决方案B:

引用一次,筛选访问: 使用此解决方案,所有对象都派生自单个类或接口(类似于IBattleElement),并存储在单个集合中。 使用元素时,将根据其类型对其进行过滤:

Update(...)
{
    IList<IActor> actors = elements.OfType<Actor>();
    foreach (IActor actor in actors) 
    {
     ITarget target = elements.OfType<Target>()[actor.TargetIndex];
     target.OnTarget(actor.IssueCommand(...));
    }
}
更新(…)
{
IList actors=elements.OfType();
foreach(演员中的IActor演员)
{
ITarget target=elements.OfType()[actor.TargetIndex];
target.OnTarget(actor.IssueCommand(…);
}
}
在那里。没有更多的冗余,但我不喜欢使用“typeof”结构,我通常会尽量避免它。显然,这在运行时性能方面更差

我在我的玩具项目中实现了解决方案A,但是在这个项目中没有任何性能关键的东西。 答案不仅要考虑性能(内存和CPU两个方面),还要考虑为我或我的设计者可能要执行的操作提供最好的设计,例如为新的行为添加新的战场元素或新接口,添加和移除战场上的物体实例,在游戏逻辑中更普遍地使用它们


我为我的例子的愚蠢表示歉意,我希望我的英语足够好,我的问题至少有一点有趣,并且不表明设计本身存在总体愚蠢。

我会选择解决方案a;额外的内存占用有效地具有指向同一对象实例的多个指针,并且代码更易于维护,以便将来扩展


顺便说一句。。。像这样的问题更多的是基于观点,所以我不认为有正确或错误的答案。例如,我可以调整解决方案A以实现IDictionary而不是IList,您不需要担心索引维护

解决方案A,但如果有理由,请不要忽略一次查询整个实例列表

一般来说(大的泛化,但有重要的先例),通过使用尽可能少的派生类型,即您所关心的接口,您将获得最大的灵活性。通过提前处理和分组实例,而不是事后将其拆分,您可能会获得最佳性能

性能增益是否重要是另一个问题。因为您可能在谈论10或100个对象(而不是数百万个),所以性能不是我首先关心的问题

从设计的角度来看,我会回避任何需要实时类型询问的问题(但这是一个意见问题)

与性能一样,在围绕内存进行设计之前先对内存进行配置。如果您使用的是VisualStudio,那么您可以使用非常好的工具来完成这项工作。假设您没有复制这些实例,那么开销无论如何都是最小的

混合方法也有意义

  • 从单个集合中添加/删除
  • 公开特定类型的集合和筛选的只读集合
  • 仅在需要时(重新)构建那些筛选的集合

公共级战场
{
私有只读IList_all=new List();
私人IReadOnlyList演员;
私人住宅;
/// 
///获取所有元素的列表。
/// 
公共IReadOnlyList所有
{
获取{return\u all;}
}
/// 
///获取已筛选的IActor元素列表。
/// 
公共IREADONLIST参与者
{
得到
{
if(_isDirty | | | u actors==null)
{
_actors=All.OfType().ToList();
}
返回演员;
}
}
/// 
///这是添加新元素的唯一方法。如果需要,这可以使线程安全。
/// 
/// 
公共无效补遗(IBattleElement)
{
_全部。添加(元素);
_isDirty=true;
}
}
…那是我的问题
public class Battlefield
{
    private readonly IList<IBattleElement> _all = new List<IBattleElement>();
    private IReadOnlyList<IActor> _actors;
    private bool _isDirty;

    /// <summary>
    ///     Gets a list of all elements.
    /// </summary>
    public IReadOnlyList<IBattleElement> All
    {
        get { return _all; }
    }

    /// <summary>
    ///     Gets a filtered list of elements that are <c>IActor</c>.
    /// </summary>
    public IReadOnlyList<IActor> Actors
    {
        get
        {
            if( _isDirty || _actors == null )
            {
                _actors = All.OfType<IActor>().ToList();
            }

            return _actors;
        }
    }

    /// <summary>
    ///     The one and only way to add new elements. This could be made thread-safe if needed.
    /// </summary>
    /// <param name="element"></param>
    public void AddElement( IBattleElement element )
    {
        _all.Add( element );
        _isDirty = true;
    }
}
var IActors = (from list in battlefieldobjects where list is IActor select list).ToList();
public static List<IActor> GetActors(this List<IBattleFieldObject> list)
{
    return list.Where(t => t is IActor).ToList();
}