【问题标题】:Best practice for an enum in a class changing a return of a function类中枚举更改函数返回的最佳实践
【发布时间】:2020-12-13 15:56:59
【问题描述】:

例如,您有一个具有声音类型的对象,并且基于枚举,特定函数会根据类型播放不同的声音

public enum ObjectType{
    Type1,
    Type2,
    Type3
}

public class Object{
    public ObjectType type;
    public Sound sound;

    private void DoThing(){

        if(type == ObjectType.Type1){
            sound = Load("sounds/sound1");
        }
        if(type == ObjectType.Type2){
            sound = Load("sounds/sound2");
        }
        //etc...

    }

}

我想我的问题是,当这个枚举的大小增长到几十或几百时,组织和测试枚举以返回另一个值的最佳做法是什么?
我应该使用开关盒而不是一堆 if 测试吗?主要是关于组织,这些函数并不占用大量资源,也不会使用大量计算,所以性能并不是我真正关心的问题,主要是可读性。

【问题讨论】:

  • 作为一种礼貌的推动,如果您发布格式正确、语法正确的代码,它会有所帮助。上面的代码无法编译,因为 'type.Type1' 不是正确的代码,并且格式难以阅读。

标签: c# if-statement enums switch-statement organization


【解决方案1】:

如果您有数百个值,枚举可能不是最好的表示,但一般来说,如果您有很多项目需要根据某个值获取,您会构建一个查找表。

您可以构建该表一次,然后只需一行代码即可根据相关枚举值获取正确的项目。

public class MyObject
{
    private ObjectType type;
    private Sound sound;

    private static Dictionary<ObjectType, Sound> soundLookup = new Dictionary<ObjectType, Sound>()
    {
        { ObjectType.Type1, Load("sounds/sound1") },
        { ObjectType.Type2, Load("sounds/sound2") },
        { ObjectType.Type3, Load("sounds/sound3") },
        // etc.
        { ObjectType.Type99, Load("sounds/sound99") },
    };

    public MyObject(ObjectType objectType)
    {
        this.type = objectType;
        this.sound = soundLookup[objectType];
    }

    private void DoThing()
    {
        var sound = this.sound;
        
        // Do something with sound.
    }

    private static Sound Load(string soundPath)
    {
        return new Sound();
    }
}

【讨论】:

  • 这种方法的一个问题是它在初始化soundLookup时不会延迟加载声音
  • 好吧,如果需要延迟加载,添加起来很简单。
  • 字典可以是&lt;ObjectType, string&gt;,只保存需要传递给Load的字符串。然后我们在需要的时候加载:this.sound = Load(soundLookup[objectType]);
【解决方案2】:

如果您能够对声音文件和ObjectTypes 进行命名约定,那么这将成为模式匹配问题。例如,如果您将枚举定义为:

public enum ObjectType
{
    Type1 = 1,
    Type2 = 2,
    Type3 = 3
}

然后您可以将您的方法替换为:

sound = Load($"sounds/sound{(int)type}");

上面的哪一行(例如,类型是Type1)会找到“sound1”

如果您无法使用枚举设置值,但名称本身仍有数字,您可以通过以下方式检索这些数字:

// Call to replace to get the number, and not the "Type" prefix
string number = Enum.GetName(typeof(ObjectType), type).Replace("Type", "");

有以下用法:

sound = Load($"sounds/sound{number}");

编辑:完整示例

public enum ObjectType{
    Type1,
    Type2,
    Type3
}

public class Object{
    public ObjectType type;
    public Sound sound;

    private void DoThing(){
        string number = Enum.GetName(typeof(ObjectType), type).Replace("Type", "");
        sound = Load($"sounds/sound{number}");    
    }    
}

【讨论】:

    【解决方案3】:

    要使其他元数据与枚举定义保持一致,您可以基于自定义属性构建字典。

    public SoundAttribute : Attribute {
        public string File { get; set; }
    }
    
    public enum ObjectType{
        [Sound(File = "sound1")]
        Type1,
    
        [Sound(File = "sound2")]
        Type2,
    
        [Sound(File = "sound3")]
        Type3
    }
    
    public static Dictionary<ObjectType, SoundAttribute> Sounds = 
        typeof(ObjectType)
        .GetFields(BindingFlags.Static | BindingFlags.Public)
        .ToDictionary(
            f => (ObjectType)f.GetValue(null), 
            f => f.GetCustomAttribute<SoundAttribute>()
        );
    

    【讨论】:

      【解决方案4】:

      这是我想要完成的更接近的实现。
      我认为它至少比我尝试做的 switch 方法要好,但我仍在尝试找出那个枚举。

      无论在实例化之前如何通过下拉列表手动设置对象类型,每次手动创建新对象类型时都会填充该下拉列表,所以我认为枚举仍然是最好的用途,但我可以错了。

          public class MyObject
          {
              public ObjectType type;
              public string soundPath;
              public string otherSoundPath;
              public Sound sound;
              public Sound otherSound;
           
              private void Start()
              {
                  type = Self.type;
                  soundPath = GetSoundPath(type, false);
                  otherSoundPath = GetSoundPath(type, true);
                  sound = Load($"\\sounds\\{soundPath}");
                  otherSound = Load($"\\sounds\\{otherSoundPath}");
              }
           
              public string GetSoundPath(ObjectType type, bool isOtherClip)
              {
                  string soundPath = string.Empty;
                  if (!isOtherClip)
                  {
                      if (!sounds.TryGetValue(type, out soundPath))
                      {
                          soundPath = "defaultClip";
                      }
                  }
                  else
                  {
                      if (!otherSounds.TryGetValue(type, out soundPath))
                      {
                          soundPath = "defaultOtherClip";
                      }
                  }
                  return soundPath;
              }
           
              private Dictionary<ObjectType, string> sounds = new Dictionary<ObjectType, string>
              {
                  { ObjectType.A, "a" },
                  { ObjectType.B, "b" },
                  { ObjectType.C, "c" },
              };
           
              private Dictionary<ObjectType, string> otherSounds = new Dictionary<ObjectType, string>
              {
                  { ObjectType.A, "a" },
                  { ObjectType.B, "b" },
                  { ObjectType.C, "c" },
              };
           
          }
           
          public enum ObjectType
          {
              A,
              B,
              C
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-03
        • 2018-12-22
        • 2013-11-09
        • 1970-01-01
        • 2017-07-23
        相关资源
        最近更新 更多