[英]Abstract Method in Non Abstract Class
我想知道在非抽象類(在 C# 中)中限制抽象方法的設計背后的原因。
我知道類實例沒有定義,因此它們不能被調用,但是當定義靜態方法時,它們也被排除在實例之外。 為什么不以這種方式處理抽象方法,有什么具體原因嗎?
它們可以在具體類中被允許,並且可以強制派生類實現方法,基本上就是在抽象類中的抽象方法的情況下所做的。
首先,我認為您的要求在邏輯上沒有意義。 如果您有abstract
方法,則基本上意味着該方法未完成(正如@ChrisSinclair 指出的那樣)。 但這也意味着整個類是未完成的,所以它也必須是abstract
的。
或者換一種說法:如果你在一個不是abstract
的類上有一個abstract
方法,那意味着你有一個不能被調用的方法。 但這意味着該方法沒有用,您可以將其刪除,並且它們都可以正常工作。
現在,我將嘗試通過一個示例更具體:想象以下代碼:
Animal[] zoo = new Animal[] { new Monkey(), new Fish(), new Animal() };
foreach (Animal animal in zoo)
animal.MakeSound();
這里, Animal
是非abstract
基類(這就是為什么我可以將它直接放入數組中), Monkey
和Fish
是從Animal
派生的, MakeSound()
是abstract
方法。 這段代碼應該做什么? 你沒有說清楚,但我可以想象幾個選擇:
您不能對類型為Animal
的變量調用MakeSound()
,只能使用類型為派生類之一的變量調用它,因此這是一個編譯錯誤。
這不是一個好的解決方案,因為abstract
的全部意義在於能夠將派生類的實例視為基類,並且仍然獲得特定於派生類的行為。 如果你想要這個,只需在每個派生類中放入一個普通的(沒有abstract
、 virtual
或override
)方法,並且不對基類做任何事情。
您不能在運行時類型實際上是Animal
的對象上調用MakeSound()
,因此這是運行時錯誤(異常)。
這也不是一個好的解決方案。 C# 是一種靜態類型的語言,因此它會嘗試在編譯時捕獲諸如“您不能調用此方法”之類的錯誤(反射和dynamic
等明顯異常),因此將其變成運行時錯誤將不適合其余部分的語言。 此外,您可以通過在引發異常的基類中創建virtual
方法來輕松完成此操作。
總而言之,您想要一些沒有多大意義的東西,並且帶有糟糕設計的味道(一個行為與其派生類不同的基類)並且可以很容易地解決。 這些都是不應實施的功能的跡象。
所以,你想允許
class C { abstract void M(); }
編譯。 假設確實如此。 當有人這樣做時,您希望發生什么
new C().M();
? 您想要執行時錯誤嗎? 好吧,通常 C# 更喜歡編譯時錯誤而不是執行時錯誤。 如果您不喜歡這種哲學,還有其他語言可用...
我認為您已經回答了自己的問題,最初沒有定義抽象方法。 因此無法實例化該類。 您是說它應該忽略它,但是根據定義,在添加抽象方法時,您是在說“由此創建的每個類都必須實現此{抽象方法}”,因此您定義抽象類的類也必須是抽象的,因為那時抽象方法仍未定義。
您可以使用“虛擬”方法實現您想要的,但使用虛擬方法可能會導致更多運行時業務邏輯錯誤,因為開發人員不會“被迫”在子類中實現邏輯。
我認為這里有一個有效的觀點。 抽象方法是完美的解決方案,因為它將“強制”定義子方法體的要求。
我遇到過很多情況,父類必須(或者更有效地)實現一些邏輯,但“只有”孩子可以實現其余的邏輯”
因此,如果有機會,我會很樂意將抽象方法與完整方法混合使用。
@AakashM,我很欣賞 C# 更喜歡編譯時錯誤。 我也是。任何人也一樣。 這是關於開箱即用的思考。
支持這一點不會影響這一點。
讓我們在這里跳出框框思考,而不是對大男孩的決定說“萬歲”。
C# 編譯器可以直接檢測並拒絕某人使用抽象類,因為它使用“abstract”關鍵字。
C# 也知道強制任何子類實現任何抽象方法。 如何? 因為使用了“抽象”關鍵字。
對於任何研究過編程語言內部的人來說,這很容易理解。
那么,為什么 C# 不能檢測到普通類中方法旁邊的“抽象”關鍵字並在編譯時處理它。
原因是它需要“返工”,付出的努力不值得支持小需求。
特別是在一個缺乏大男孩給他們的框框思考的人的行業。
抽象類可能包含抽象成員。 如果任何方法具有我們無法在同一個類中實現的抽象關鍵字,則只有方法聲明。 所以抽象類是不完整的。 這就是為什么沒有為抽象類創建對象的原因。
非抽象類不能包含抽象成員。
例子:
namespace InterviewPreparation
{
public abstract class baseclass
{
public abstract void method1(); //abstract method
public abstract void method2(); //abstract method
public void method3() { } //Non- abstract method----->It is necessary to implement here.
}
class childclass : baseclass
{
public override void method1() { }
public override void method2() { }
}
public class Program //Non Abstract Class
{
public static void Main()
{
baseclass b = new childclass(); //create instance
b.method1();
b.method2();
b.method3();
}
}
}
目前尚不清楚您為什么要這樣做,但另一種方法可能是強制派生類提供委托實例。 像這樣的東西
class MyConcreteClass
{
readonly Func<int, DateTime, string> methodImpl;
// constructor requires a delegate instance
public MyConcreteClass(Func<int, DateTime, string> methodImpl)
{
if (methodImpl == null)
throw new ArgumentNullException();
this.methodImpl = methodImpl;
}
...
}
(當然,簽名string MethodImpl(int, DateTime)
只是一個示例。)
否則,我可以推薦其他答案來解釋為什么您的願望可能不會讓世界變得更美好。
所以上面的答案是正確的:擁有抽象方法使類本質上是抽象的。 如果你不能實例化一個類的一部分,那么你就不能實例化這個類本身。 但是,上面的答案並沒有真正討論您的選擇。
首先,這主要是公共靜態方法的問題。 如果這些方法不打算公開,那么您可以保護非抽象方法,這些方法在抽象類聲明中是允許的。 因此,您可以將這些靜態方法移至單獨的靜態類,而不會出現太大問題。
作為替代方案,您可以將這些方法保留在類中,但隨后不使用抽象方法,而是聲明一個接口。 本質上,您有一個多重繼承問題,因為您希望派生類從兩個概念上不同的對象繼承:一個具有公共靜態成員的非抽象父類,以及一個具有抽象方法的抽象父類。 與其他一些框架不同,C# 確實允許多重繼承。 相反,C# 提供了一個正式的接口聲明來滿足這個目的。 此外,抽象方法的全部意義實際上只是強加了某個概念接口。
我有一個與 OP 試圖實現的非常相似的場景。 在我的情況下,我想要抽象的方法將是一個受保護的方法,並且只有基類知道。 所以“新的 C().M();” 不適用,因為所討論的方法不是公開的。 我希望能夠在基類上實例化和調用公共方法(因此它必須是非抽象的),但是我需要這些公共方法來調用子類中受保護方法的受保護實現並且沒有默認實現在父母。 從某種意義上說,我需要強制后代覆蓋該方法。 由於依賴注入,我不知道編譯時子類是什么。
我的解決方案是遵循規則並使用具體的基類和虛擬保護方法。 但是,對於默認實現,我拋出 NotImplementedException 錯誤“必須在子類的實現中提供方法名稱的實現”。
protected virtual void MyProtectedMethod()
{
throw new NotImplementedException("The implementation for MyProtectedMethod must be provided in the implementation of the child class.");
}
通過這種方式,永遠不能使用默認實現,並且后代實現的實現者很快就會發現他們錯過了重要的一步。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.