【问题标题】:Seeking an elegant way for inherited classes to have a "StateName" property为继承的类寻求一种优雅的方式来拥有“StateName”属性
【发布时间】:2014-10-04 18:37:33
【问题描述】:

请参阅下面的解决方案 - 搜索 UPDATE。

我有一个广泛的状态机架构,我通过为每个状态创建一个类来实现它(有多个机器,但所有的状态都继承自同一个“MachineState”类)。每个状态都有一个静态属性“StateName”:

public class SomeState: MachineState
{

  // THIS BLOCK SHOULD BE COPIED TO ALL STATE CLASSES!!
  private static string _StateName;
  public static string StateName
  { 
     get {
           if (_StateName == null)
           { 
               _StateName = MethodBase
                  .GetCurrentMethod()
                  .DeclaringType
                  .ToString()
                  .Split(new char[] { '.' })
                  .ToList()
                  .Last();
           }
           return _StateName; 
          }  
  }
  // END OF BLOCK


  public SomeState(Queue<string> messages)  // 
        : base(messages)
  {
       ...
  }
...
}

呃。

至少我会调用处理器密集型的东西,以便每个类只获得一次名称——就我的目的而言,这是可以接受的成本。但我真的很想为他们找到某种方式来“继承”这段代码——或者至少是某种方式让他们将它包含在宏之类的东西中。我有一个抽象属性,所以如果它没有实现,我会在编译时捕获它;但是,仍然必须有一种方法可以避免将这种混乱复制到每个班级中——然后如果需要,必须在每个班级中更改它。

有什么想法吗?

谢谢。

------------ 更新 ---------------------- -----

生活充满了妥协;这个我可以忍受。 @Tallek 在基类中提出了这个建议:

 public static string GetStateName<T>() where T : MachineState
    {
        return typeof(T).Name;
    }

我将它与我的静态属性集成,如下所示(对于“SomeState”类):

    // THIS BLOCK SHOULD BE COPIED TO ALL STATE CLASSES!!
    public static string StateName { get { return GetStateName<SomeState>(); } }

它并不完美;我必须确保在每个类的 GetStateName 调用中获得正确的州名。但它做了两件我急于做的事情:将逻辑移动到一个位置,并且更易于阅读。保持 StateName 抽象将帮助我捕获任何尚未实现 StateName 的状态。

再次感谢大家。

【问题讨论】:

  • 必须是static吗?如果不是,那么它被继承。
  • 是的;我试图避免使用庞大的枚举或数以千计的 const 字符串来识别状态名称,所以我希望每个状态都是可识别的(作为字符串......这是必要的)。所以我希望能够访问这样的状态: if(stateName == SomeState.StateName) { }
  • public string StateName { get { return GetType().Name; } }?
  • 然后显示您访问此属性的代码,我不清楚您要如何处理此属性值。你可能想反过来做,使用工厂和开关或字典,比如case "SomeState": return new SomeState();
  • 可以在基类上做一个静态方法,GetStateName() where T : MachineState and return typeof(T).Name;

标签: c# class inheritance macros types


【解决方案1】:
  • 你有一个状态
  • 你的状态是类
  • 您想将该状态与另一个状态进行比较
  • 您不想为了比较而实例化状态

我不认为你这样做比:

if(state.GetType() == typeof(SomeState))

【讨论】:

  • 再次,当我进行比较时,我实际拥有的是包含状态名称的消息——而不是实例化状态。我的名字实际上是机器被请求进入的状态,而不是当前状态。它从控制机器的应用程序到达消息队列——我也希望能够通过“SomeState.StateName”访问名称。
  • 以后用数字;如果你可以创建一个类,你可以创建一个枚举成员来配合它......你不能做得比这更好。
【解决方案2】:

它不是很好,但也许值得考虑......

public class MachineState
{
   public static string GetStateName<T>() where T : MachineState
   {
       return typeof(T).Name;
   }

}

这样使用:

if("MyState" == MachineState.GetStateName<MyState>()) { ... }

【讨论】:

  • 好的,我现在明白您在上面的评论中所说的内容了。我得做一些实验。我认为如果我使该方法内联编译,它可能是可以接受的性能。它仍然必须在每次比较时调用 typeof(T) ——这很多。我宁愿从堆中抓取一个静态字符串。但这比我考虑过的其他任何东西都要优雅得多。谢谢。
  • 没问题。就 typeof() 而言,我相信这是在编译时解决的。我认为您不会因此而受到运行时性能的影响。
【解决方案3】:

您可以使用扩展方法完成此操作:

public static string GetStateName(this MachineState state) {

  return state.GetType().Name;

}

像这样使用它:

if(state.GetStateName() == "SomeState") { /* Do something */ }

如果您愿意,可以使用自己的缓存:您可以在此处访问所需的任何静态结构。

【讨论】:

    【解决方案4】:

    我可能同意 CodeCaster。对于枚举而言,20 或 30 个州并没有那么大。

    根据您对接收消息并识别该消息的处理程序的描述,并结合查看您的示例:

    if(stateName == SomeState.StateName) { }
    

    这意味着您将stateName 作为参数。所以你对每个状态都有一个 if 块,这样你就可以确定消息适用于哪个状态?

    if(stateName == SomeState.StateName) { 
    
    }
    if(stateName == OtherState.StateName) { 
    
    }
    

    如果是这样的话...... (如果给出有限的用例信息,那么这个答案的其余部分是基于该前提的,所以如果其余部分不适用,请不要激怒我)

    您希望所有类都自动具有此 StateName 属性。这看起来很干,但是我们看到您仍然必须为每个状态设置一个 if 块,因为该 IMO 有更多代码,所以干的更少。您已将一个 DRY 换成另一个 DRY。

    我会有枚举,每个枚举都有一个

    public enum States {
    ...
    [Handler(typeof(SomeState))]
    SomeState = 5,
    ...
    

    结合工厂模式,现在你扔掉所有的 if 块,只需要调用你的工厂:

    MachineState newState = StateFactory.Create(stateName);
    

    工厂使用Enum.Parse将stateName转换为枚举,从中访问属性获取需要实例化的类型。 不需要 switch/case/if/else。

    这意味着每次你实现一个新的状态类,你只需要接触一个地方,那就是枚举,并且代码重复最少。

    如果每个 if 块中都有针对该特定状态的特定逻辑 将该代码移动到在 MachineState 或 IMachineState 接口中定义的 HandleMessage 方法中,该方法为每个 SomeState 执行特定于该状态的操作。假设您的消息指示 stateName,并且消息中可能有一些“内容”或“数据”需要处理:

    MachineState newState = StateFactory.Create(stateName);
    newState.HandleMessage(messageContent);
    

    我意识到它可能比这更复杂。您可能需要将状态与状态处理分离到单独的类中以使其正常工作。很难说。如果我站在你的立场上,我肯定会认真考虑这一点。

    【讨论】:

    • 有趣。我不是 C# 的铁杆家伙,这个工厂业务不是我搞砸的;这听起来像是一个很好的机会来玩它。谢谢!
    • 顺便说一句,20 或 30 将是每台机器的枚举。我正在为一个复杂的系统建模,该系统最终将拥有数十台机器;所以我正在寻找尽可能简化或标准化的方法。任何可以继承的东西对我来说都具有巨大的价值,因为它可以节省我做 int 数百次的时间。即使是像创建枚举值这样简单的事情,如果我能提供帮助,我宁愿不这样做。
    • @JoeBaker 这意味着您正在重复if(stateName == *State.StateName) { 形式的代码数百次?我更喜欢取消这些 if's/switch/cases 列表并使用一个通用接口并让每个 State 类表达自己的行为,例如 HandleMessage。当您复制粘贴该 if 块以支持新的 State 类时,您可能会错过更改其中某些部分的类名,假设您在某处也有一个 new SomeState 也必须更改。如果你对一个新的枚举值做同样的事情,编译器会捕捉到这个重复。
    猜你喜欢
    • 2016-01-23
    • 2011-10-15
    • 2011-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-09
    • 2021-08-14
    • 2014-02-10
    相关资源
    最近更新 更多