在C#中使用GetType/instanceof与其他选项

在C#中使用GetType/instanceof与其他选项,c#,oop,object,reflection,instanceof,C#,Oop,Object,Reflection,Instanceof,我在C#游戏中遇到了一个问题。这是一个简单的基于瓷砖的匹配游戏,我正在尝试制作的一个加电游戏出现了问题: 假设我们有基本的瓷砖类型,圆形,正方形和菱形,它们都是瓷砖的子类。我尝试将“匹配”行为提取到一个抽象的Tile方法:canMatchWith(Tile t),而不是让圆只与圆匹配。 瓷砖还有两种方法可以添加/删除它们可以匹配的瓷砖 所以我们在游戏的中间有一个圆形的瓦片,我们有一个强大的力量,说“圆形的瓦片可以与这一回合的方形瓦片相匹配”。我会浏览所有的圆形瓷砖,然后说circleTile.a

我在C#游戏中遇到了一个问题。这是一个简单的基于瓷砖的匹配游戏,我正在尝试制作的一个加电游戏出现了问题:

假设我们有基本的瓷砖类型,圆形,正方形和菱形,它们都是瓷砖的子类。我尝试将“匹配”行为提取到一个抽象的Tile方法:canMatchWith(Tile t),而不是让圆只与圆匹配。 瓷砖还有两种方法可以添加/删除它们可以匹配的瓷砖

所以我们在游戏的中间有一个圆形的瓦片,我们有一个强大的力量,说“圆形的瓦片可以与这一回合的方形瓦片相匹配”。我会浏览所有的圆形瓷砖,然后说circleTile.addCanMatchWith(typeof(Square))。在内部,我们有一个可以匹配的列表

后来,我想说“圆不能再与正方形匹配”,简单地说是circleTile.removeCanMatchWith(typeOf(Square))

这是我目前的解决方案,它工作得很好,没有我注意到的性能缺陷(这是一个基于分片的匹配游戏,所以这些类型在每次“移动”时只评估一次,而不是逐帧)。然而,我脑海中的声音告诉我,这是一个不好的方式来完成这种行为。所以我有一些选择:

  • 枚举。。。每个Tile都可以由一个Tiletype类型变量组成。这将在构造函数中初始化,并设置为Type.SQUARE,以此类推。然后,每个磁贴都会有一个canMatchWith列表,其功能与我最初的实现相同。除此之外,这有点棘手。假设我有一些圆的子类,oval和elipse。我希望椭圆只能与正方形匹配,但省略号可以与所有圆匹配,而不能与正方形匹配 这里的问题是冗余,我的枚举现在也将有OVAL和ELIPSE,而ELIPSE类将有(CIRCLE、OVAL、ELIPSE-TileTypes)作为它可以匹配的类型。这是完全多余的,我只想说“圆”,我可以用类型来表示。我认为tile可以有TileType baseType和TileType actualType

  • 某种形式的行为构成。忘记Tile子类,只需为List提供Tiles方法和实例变量。然后,在运行时,我们可以只说someTile.addCanMatch(newCircleMatchBehavior())。这看起来很傻,因为我会有很多课程只是说你可以匹配一个特定的形状
  • 总之,我试图实现的是让多个对象类型能够与任意数量的不同类型交互。问题是,我应该为类型使用什么。在这里使用GetType可以吗?枚举?还是有人会推荐更好的策略?我试图尽可能的笼统,这些互动程序不应该对其他互动程序有任何硬编码的依赖性,并且必须能够改变它们可以动态地与谁交互。假设我创建了一个新的Tile子类,五角大楼。。。五边形可以与正方形、圆形和五边形相匹配。我的实现很简单,但有些东西告诉我这是一个肮脏的OOP实践


    我觉得我必须使用类型/枚举,因为我不想说thisTile.addCanMatch(Tile someOtherObject)。这太具体了,我希望此互动程序能够与作为特定类实例的所有互动程序匹配

    如果相似类型的所有形状都将始终共享一个行为,那么不将该行为存储在“每个实例”级别是有意义的。相反,您可以使用“CanMatchManager”,它存储列表字典,并按形状类型索引。然后,当圆尝试比较匹配时,它会从MatchManager请求可以匹配的类型。或者,MatchManager可以采用这两种形状,并确定它们是否匹配。这是

    我知道这个问题已经得到了回答和接受,但我曾经做过类似的事情,我想我应该把代码贴在这里

        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
    Tile
    ,然后设置某种
    行为
    属性。棋盘上的所有圆圈可能都可以共享相同的
    行为
    对象,然后当规则更改一圈时,您可以在该
    行为
    对象上设置更改。如果我理解情况,这可能会减少代码重复。如果我理解正确,这有点像我的#2行为组合。我担心的是行为爆炸。例如,与圆形和方形行为匹配。明白我的意思了吗?如果有5种类型的瓷砖,那么我们有5种选择1+5选择2+5选择3+5选择4+5选择5种行为组合。这似乎比GetType更糟糕。然而,我可能误解了你建议中的某些内容。瓷砖本身可以做任何他们想做的事情,一个可以做爆炸动画,等等。这只适用于瓷砖可以与另一个匹配的情况。我不是建议行为是枚举或不可修改的类。您可以用不同的方式对它进行子类化,但主要是我希望它有一个内部集合,表示它的类型(圆形)和它可以匹配的类型(方形)。然后,您可以以某种面向数据的方式来更改它。我完全同意为了良好的对象设置应该避免任何会导致大量if/else/switch块的事情,但是我们也希望避免编写新的类来解释行为上的一些不太新的变化。有趣的是,比如:CanMatchManager::canMatch(Tile one,Tile two){return dictionary[one]。contains(two)}其中dictionary将平铺类型映射到可匹配类型的列表。然后,如果我想添加一个交互,我可以说tileManager.addInteraction(Tile one,Tile two),它会更新为{dict[one].add(two),dict[two].add(one)}。这似乎比wha更有效
            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