【问题标题】:How to force a derived class to include certain properties with default value如何强制派生类包含具有默认值的某些属性
【发布时间】:2011-01-10 14:39:11
【问题描述】:

我有一个角色扮演游戏的类结构,看起来像这样......

public abstract class Item
{
   public abstract string Name { get; set; }
}

public abstract class Armor : Item
{
   public override string Name { get; set; }
}

public class Helmet : Armor
{
   public override string Name { get; set; }
}

基本上,我试图强制每个派生类型包含一个“名称”属性。这是最好的方法吗?我知道我可以从 Item.Name 中删除“抽象”,然后删除 Armor 和 Helmet 中覆盖的“名称”属性。如果这样做,代码看起来会更简洁一些,但我可能会忘记在这些派生类中设置 base.Name。

有人可以帮我看看最好的方法吗?

编辑: 对不起,让我再澄清一下我的问题。我想确定两件事。 1)名称属性存在于所有派生类中 2) Name 属性不为 null 或为空

我基本上想强制从 Item 派生的任何类(并且不是抽象的)具有 Name 的值。

【问题讨论】:

    标签: c# abstract-class derived-class


    【解决方案1】:

    我认为您仍然可以在抽象类中使属性为虚拟,因此应该可以解决您的问题。

    您可以将值设置为基础抽象类中的特定值, 这是一个例子:

    public abstract class Item
    {
       public virtual string Name 
       { 
             get {return m_strName;} 
             set {m_strName = value;}
       }
    
    public abstract class Armor : Item
    {
       public override string Name { get; set; } // if you want to override it 
    }
    
    public class Helmet : Armor
    {
       public override string Name { get; set; } // if you want to override it
    }
    

    【讨论】:

      【解决方案2】:

      听起来您很担心初始化属性?

      但我可能会忘记设置 base.Name 在这些派生类中。

      您可以强制设置 Name 属性的一种方法是将此设置器包含在您的基类构造函数中,如下所示:

      public class MyBaseClass
      {
          private string _name;
      
          public MyBaseClass(string name)
          {
              _name = name;
          }
      }
      

      那么从 MyBaseClass 派生的所有东西都必须满足该构造函数:

      public class MyDerivedClass
      {
          public MyDerivedClass(string name) : base(name)
          {
      
          }
      }
      

      那么你也可以使属性:

      • 抽象以确保它存在于每个具有自己实现的派生类中
      • 虚拟提供基本实现以及所有要被覆盖的实现。

      我不打算冒险上面是否是好的设计,但它会确保所有派生类在实例化时都具有有效的名称属性。

      正如其他答案所暗示的,另一种方法是实现一个在其基本实现中引发异常的虚拟属性。

      【讨论】:

      • 我刚刚输入了相同的内容(几乎完全一样!),但你赢了 40 秒 :D
      • @David 你是说强制所有派生类都具有有效的名称属性可能是糟糕的设计吗?或者这种做法可能是糟糕的设计?如果是前者,你能解释一下为什么吗?我只是认为对派生类执行一些规则是一件好事。
      • @mikedev - 我以前从未实现过这种类型的模式,所以我不确定它是否是糟糕的设计。我通常需要几天甚至几周的时间来研究一种新模式,然后才能对它的优点做出自己的决定(以及他们谁说我是对的)。不过我不认为这里有什么问题,如果你采取 is-a 继承的观点,Armor is-a Item,并且所有的项目都必须有一个名称,这样就可以实现。它符合要求,并且没有任何明显的缺点,除了强制所有未来的项目都有名字。
      【解决方案3】:

      Mike 是对的,如果您只想在所有派生对象上使用“名称”属性,则根本不需要将其标记为抽象,因为它是继承的。

      如果您只想强制在创建项目时确定名称,您可以通过隐藏零参数构造函数并公开接受名称的构造函数来强制它。

      看看这段代码:

      public class Item
      {
         public string Name { get; set; }
      
         public Item(string name)
         {
             this.Name = name;
         }
      
         protected Item() {}
      }
      
      public class Armor : Item
      {   
         public Armor(string name) : base(name) {}
         protected Armor() {}
      }
      
      public class Helmet : Armor
      {   
         public Helmet(string name) : base(name) {}
         protected Helmet() {}
      }
      

      以上定义意味着:

      Helmet myHelmet = new Helmet(); //will not build
      Helmet myHelmet = new Helmet("Some Fancy Helmet Name"); //will build
      
      Armor myArmor  = new Armor (); //will not build
      Armor myArmor  = new Armor ("Some Fancy Armor Name"); //will build
      
      Item myItem = new Item (); //will not build
      Item myItem = new Item("Some Fancy Item Name"); //will build
      

      这强制类的任何实例必须在创建时定义名称。无论如何,一种可能的解决方案...

      【讨论】:

        【解决方案4】:

        所以这取决于你想做什么......

        将类抽象化会强制所有子类实现该类(及其抽象函数等),但如果您希望函数具有基本功能并可以覆盖该函数,那么我建议不要将类抽象化,将特定函数变为虚拟,这样当虚拟函数没有被覆盖时,就会调用基函数。

        并且总是可以选择创建具有相同名称的“新”属性,但我认为这不是一个好习惯。

        希望有帮助。

        【讨论】:

        • 我需要抽象类的一个原因是因为我从不想创建 Item 或 Armor 的实例。在此示例中,只有“Helmet”的派生类型是有效对象
        【解决方案5】:

        会使 Name 成为虚拟的 ie。 public virtual Name {get; set; } 访问器可以在 Item 类中使用吗?由于 Helment 和 Armor 从 Item 类下降。它将强制它们必须被覆盖......

        希望这会有所帮助, 最好的祝福, 汤姆。

        【讨论】:

        • virtual 关键字仅指定成员“可以”被派生类型修改(即覆盖),而不是必须。
        • @KevinP:也许你是对的,但 OP 说“但我可能忘记在这些派生类中设置 base.Name”,你怎么看?
        • 我编辑了我的问题以使其更清楚。我想确保 Name 在所有派生类中始终具有值。但也许这不是我可以从基类级别强制执行的事情?
        【解决方案6】:

        如果这样做,代码看起来会更简洁一些,但我可能会忘记在这些派生类中设置 base.Name。

        那么对象的名称最终会变得很愚蠢。

        更好的是,让name 以null 开头。然后,如果您忘记初始化名称但有人尝试使用它,您会得到一个异常,并且您会知道您必须修复它。

        【讨论】:

          【解决方案7】:

          只需要在基类中定义Name,不需要指定为抽象。它仍可作为所有派生类中的属性使用。

          public abstract class Item
          {
             public string Name { get; set; }
          }
          
          public abstract class Armor : Item
          { }
          
          public class Helmet : Armor
          { }
          

          【讨论】:

          • 我遇到了和上面一样的问题。主要关注的是值应该被分配并且永远不应该是空白的。你的解决方案没有强制执行
          猜你喜欢
          • 2019-07-03
          • 1970-01-01
          • 2020-05-22
          • 2012-02-23
          • 2017-02-04
          • 2014-07-01
          • 2019-12-19
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多