2010-01-08 88 views
9

初始化繼承類中常量或其他字段的最佳方法是什麼?我意識到這個例子中有很多語法錯誤,但這是解釋我想要做什麼的最好例子。初始化繼承類中的字段

public abstract class Animal { 
    public abstract const string Name; // #1 
    public abstract const bool CanFly; 
    public abstract double Price; // price is not const, because it can be modified 

    public void Fly() { 
    if (!CanFly) 
     Debug.Writeln("{0}s can't fly.", Name); 
    else 
     Debug.Writeln("The {0} flew.", Name); 
    } 
} 

public class Dog : Animal { 
    public override const string Name = "Dog"; // #2 
    public override const bool CanFly = false; 
    public override double Price = 320.0; 
} 

public class Bird : Animal { 
    public override const string Name = "Bird"; 
    public override const bool CanFly = true; 
    public override double Price = 43.0; 
} 

有幾件事情,我試圖完成:

  • 基類必須分配這3個領域。
  • 理想情況下,我希望這些初始化的字段在類的頂部一起,以便我可以看到哪些常量分配給每個類,並在需要時更改它們。
  • 字段名稱和CanFly無法更改。

我知道你可以在構造函數中初始化這些字段(如果你擺脫了const),但是不能保證它們被賦值。如果您將這些字段改爲屬性並覆蓋它們,則仍需要初始化屬性的後臺字段。你將如何實現這一點?

它抱怨

幾個語法錯誤:

  • 修飾語「抽象」是不是在場上纔有效。嘗試使用屬性來代替。 (#1)
  • 常量字段要求要提供一個值(#1)
  • 修飾符「覆蓋」是不適用於這個項目(#2)
+3

我認爲你誤解了常量。常量是在整個宇宙歷史中永遠不會改變的值;因此,名稱爲「常量」。常量的好例子:十幾個雞蛋的數量,鉛的原子數。不好:你的應用程序的版本號;這隨着時間而改變。可怕的是:每桶石油的價格;每隔幾秒更換一次。如果有一個字段的值在應用程序的生命週期中可能發生變化,那麼它不是一個常量。 – 2010-01-09 01:20:01

+0

感謝您的解釋。我想我有點困惑,因爲Dog.Name和Dog.CanFly字段不應該改變,因此我認爲它們應該是常量。然而,Animal.Name和Animal.CanFly字段將被繼承類改變,所以它們不應該是常量。我試圖將它們定義爲僅在繼承類中的常量,我猜你不能這樣做。 – Senseful 2010-01-09 17:43:40

+0

要對在施工時設置一次並且之後從未更改過的值進行建模,請使用只讀字段。只讀字段可以是實例字段,在這種情況下,每個實例可以有不同的值,或者靜態的,在這種情況下,每個實例共享相同的值。 – 2010-01-09 19:39:15

回答

19

如果一個基類需要由派生類中提供一個值,這兩個最常見的方法是:

要求它在構造函數中:

public readonly double Price; 

protected BaseClass(double price) 
{ 
    this.Price = price; 
} 

派生類必須通過價格到構造函數:

public Derived() : base(32) 
{ 
} 

或者,使它抽象屬性:

public abstract double Price { get; } 

派生類必須提供一些方法返回值(儘管他們得到它是由派生類,這在許多情況下,提供了更多的靈活性):

public override double Price 
{ 
    get 
    { 
     return 32; 
    } 
} 
+1

抽象屬性的潛在缺點是派生類可能會破壞「永不改變生命期」保證,這可以通過readonly field + ctor來強制執行。 – 2010-01-13 02:33:03

5

如果你想擁有特別的字段在你的抽象類中,但不想在基類中定義它們,你可以要求實現者通過構造函數提供值。

public abstract class MyClass 
{ 
    private readonly double price; 

    protected MyClass(double price) 
    { 
     this.price = price 
    } 
} 

利用這樣的基類,所有派生類必須提供一個值給基類的構造函數,或代碼不會編譯。這裏有一個如何可能看一個例子:

public class Foo : MyClass 
{ 
    public Foo() : base(42) { } 
} 
1

您可以使用readonlyinternal構造函數來強制執行此操作,但在中文中會略顯缺乏。如果你這樣做,繼承者需要在同一個命名空間/程序集中才能利用內部構造函數。

0

另一種模式是創建一個虛擬的訪問器(屬性)。這迫使派生類實現它。

這樣做的好處是,'can'可能有一個答案,取決於(說)一天的時間。

1

你得到的錯誤或多或少告訴你該怎麼做。你想要使用抽象屬性,而不是字段,對於NameCanFly,而Price將是一個正常的屬性。在派生類中,抽象屬性將是隻讀的,並且每種情況下都會返回一個常量。代碼:

public abstract class Animal { 
    public abstract string Name { get; } 
    public abstract bool CanFly { get; } 
    public double Price { get; set; } 
    // etc 
} 

public class Dog : Animal { 
    public override string Name { get { return "Dog"; } } 
    public override bool CanFly { get { return false; } } 
    public Dog() { Price = 320.0; } 
} 

// etc