在 C# 中使用 GetType/instanceof 与替代方案

Using GetType/instanceof in C# vs. alternatives

我在用 C# 制作的游戏中遇到了一个问题。这是一个简单的基于图块的配对游戏,问题出现在我尝试制作的能量提升上:

假设我们有基本的瓷砖类型、圆形、正方形和菱形,它们都是瓷砖的子class。我尝试将 "matches" 行为提取到一个抽象的 Tile 方法:canMatchWith(Tile t),而不是让圆仅与圆匹配。 Tiles 还有两种 add/remove 可以匹配的 Tiles 方法。

假设我们在游戏中间有一个圆形方块,并且我们有一个显示 "Circle tiles can match with square tiles this turn" 的道具。我会遍历所有圆形图块并说 circleTile.addCanMatchWith(typeof(Square))。在内部,我们有一个 List canMatchWith。

那后面想说"Circles can no longer match with squares"就简单说circleTile.removeCanMatchWith(typeOf(Square))。

这是我目前的解决方案,它运行良好,没有我注意到的性能缺陷(这是一个基于图块的匹配游戏,所以这些类型每 'move' 只评估一次,而不是逐帧评估).但是,我脑海中的声音告诉我,这是完成此行为的糟糕方法。所以我有一些选择:

  1. Enums... 每个 Tile 都可以由一个 Tiletype 类型变量组成。这将在构造函数中初始化并设置为 Type.SQUARE 用于正方形,等等。然后,每个Tile都会有一个List canMatchWith,功能和我原来的实现是一样的。除了在这种情况下,它有点棘手。假设我有一些圆形子classes、椭圆形和椭圆形。我希望椭圆形只能匹配正方形,但椭圆形可以匹配所有圆圈而不是正方形。

这里的问题是冗余,我的枚举现在也会有 OVAL 和 ELIPSE,而 Elipse class 会有 (CIRCLE, OVAL, ELIPSE TileTypes) 作为它可以匹配的类型。这完全是多余的,我只想说 "Circle" 我可以用这些类型。我想 Tiles 可能有 TileType baseType 和 TileType actualType。

  1. 某种形式的行为组合。忘记 Tile subclasses,只需提供 Tiles 方法和 List 的实例变量。然后,在运行时我们可以只说 someTile.addCanMatch(new CircleMatchBehavior())。这看起来很愚蠢,因为我会有一堆 classes 只是说你可以匹配特定的形状。

总而言之,我想要完成的是让多个对象类型能够与任意数量的不同类型进行交互。问题是,我应该为 Type 使用什么。这里用GetType可以吗?枚举?还是有人会推荐更好的策略?我试图尽可能通用,这些磁贴不应该对其他磁贴有任何硬编码依赖性,并且必须能够动态更改它们可以与之交互的对象。假设我制作了一个新的 Tile subclass,pentagon...好吧,Pentagons 可以与 Squares、Circles 和 Pentagons 匹配。我的实施很容易,但有些事情告诉我这是一种肮脏的 OOP 做法。

我觉得我必须使用 Types/Enums 因为我不想说 thisTile.addCanMatch(Tile someOtherObject)。这太具体了,我希望 thisTile 能够与作为特定 class 实例的所有图块匹配。

如果相似类型的所有形状总是共享一个行为,那么不将该行为存储在 'per instance,' 级别上是有意义的。相反,您可以使用 'CanMatchManager,' 存储列表字典,按形状类型索引。然后,当一个圆尝试比较匹配项时,它会从 MatchManager 请求它可以匹配的类型。或者,MatchManager 可以接受这两个形状,并确定它们是否匹配。这是Mediator Pattern

我知道这个问题已经得到回答和接受,但我已经做过一次这样的事情,我想我只是 post 这里的代码。

    public class TypeMatchManager
    {
        private Dictionary<Type, List<Type>> savedMatches = new Dictionary<Type, List<Type>>();

        public TypeMatchManager() { }

        public void AddMatch(Type firstType, Type secondType)
        {
            this.addToList(firstType, secondType);
            this.addToList(secondType, firstType);
        }

        public void DeleteMatch(Type firstType, Type secondType)
        {
            this.deleteFromList(firstType, secondType);
            this.deleteFromList(secondType, firstType);
        }

        public bool CanMatch(Type firstType, Type secondType)
        {
            List<Type> firstTypeList = this.findListForType(firstType);
            List<Type> secondTypeList = this.findListForType(secondType);
            return (firstTypeList.Contains(secondType) || secondTypeList.Contains(firstType));
        }

        private void addToList(Type firstType, Type secondType)
        {
            var matchingTypes = this.findListForType(firstType);
            if (!matchingTypes.Contains(secondType))
            {
                matchingTypes.Add(secondType);
            }
        }

        private void deleteFromList(Type firstType, Type secondType)
        {
            var matchingTypes = this.findListForType(firstType);
            if (matchingTypes.Contains(secondType))
            {
                matchingTypes.Remove(secondType);
            }
        }

        private List<Type> findListForType(Type type)
        {
            foreach (var keyValuePair in savedMatches)
            {
                if (keyValuePair.Key == type)
                {
                    return keyValuePair.Value;
                }
            }
            savedMatches.Add(type, new List<Type>());
            return findListForType(type);
        }
    }

class 的设计使您提供的参数类型无关紧要;它检查 type1.list 有 type2 和 type2.list 有类型。

一个简单的例子:

        typeManager.AddMatch(a, b);
        Console.WriteLine(typeManager.CanMatch(a, b)); // True
        typeManager.DeleteMatch(b, a);
        Console.WriteLine(typeManager.CanMatch(a, b)); // False
        Console.WriteLine(typeManager.CanMatch(a, c)); // False
        typeManager.AddMatch(a, c);
        Console.WriteLine(typeManager.CanMatch(a, c)); // True
        Console.WriteLine(typeManager.CanMatch(a, d)); // False
        typeManager.AddMatch(b, d);
        Console.WriteLine(typeManager.CanMatch(a, d)); // False
        Console.WriteLine(typeManager.CanMatch(d, b)); // True
        typeManager.DeleteMatch(d, b);
        Console.WriteLine(typeManager.CanMatch(d, b)); // False
        Console.WriteLine(typeManager.CanMatch(b, d)); // False