問題的目的是瞭解繼承如何在內存中工作;我知道它是什麼以及何時使用它。C#.NET - 繼承 - 在內存中創建的對象的數量
以下是用例 -
class A {}
class B : A {}
class C
{
B b = new B();
}
現在,有多少個對象(排除一個爲C級,因爲這將是切入點和任何違約DOTNET的/ CLR對象)在內存中創建?它是兩個(一個是A,另一個是B)?或者只有一個B也包含A的成員?一些解釋會有所幫助。
問題的目的是瞭解繼承如何在內存中工作;我知道它是什麼以及何時使用它。C#.NET - 繼承 - 在內存中創建的對象的數量
以下是用例 -
class A {}
class B : A {}
class C
{
B b = new B();
}
現在,有多少個對象(排除一個爲C級,因爲這將是切入點和任何違約DOTNET的/ CLR對象)在內存中創建?它是兩個(一個是A,另一個是B)?或者只有一個B也包含A的成員?一些解釋會有所幫助。
既然你的問題的目的似乎是基本上看到使用構圖和繼承之間的「物理」差異,我將關注這一點。
當您使用new
時,將創建一個類型的單個實例,並且該類型的適當構造函數(及其所有「父項」)被執行一次。
在C#中,默認繼承方法(B : A
類型)是的子類化。在這種方法中,派生類基本上是它的父類的副本,加上派生類的實例字段以及與例如與其關聯的元數據。任何新的虛擬方法。
這意味着在你的情況下,調用new B()
只會創建一個對象實例,就是這樣。 B
的實例本身包含A
的字段和元數據,但不包含參考到A
的實例。
如果定義B2
這樣的:
class A2
{
int myInt;
}
class B2
{
A2 aInstance = new A2();
}
然後B2
構造還創建的A2
一個實例,因此你有兩個對象實例,A2
類型之一,另一個B2
類型。 B2
僅包含對A2
實例的引用,而不是其副本。
這是如何轉化爲運行時成本的?
A2
傾向於分配在B2
之後。結果呢?那麼,我不認爲這是你需要提前關心的事情。有額外的實例需要花費,但除非你分配了數百萬個實例,否則它可能不會帶來很大的可觀察的差異。如果你的應用程序允許它,你甚至可以獲得淨收益,因爲組合模型可以讓你在多個地方重複使用同一個實例,這對於子類來說是不可能的。有時這是有道理的,有時它不會:)
當然,請注意,您並不總是必須使用類。例如,A2
可以很容易地成爲struct
,消除了額外的實例 - 再次,不可能與子類化,因爲struct
s不能被繼承。在這種情況下,這兩種方法相當。
與通常情況下的性能一樣,您確實需要做實際的分析才能得到您的答案。結果可能會像「99.9%的代碼執行得很好,但如果我們將其更改爲struct
並將多態性移到更高層,這一類可以爲我們節省大量CPU/RAM」。
最後,我很確定這些都不是合同的一部分。如果微軟決定在未來的.NET框架版本中改變繼承方式,並且創建一個新的實例,而不是「內聯」父代,我認爲它不會以任何方式違反規範。除非你絕對需要需要來依賴這個信息,否則不要。
如果忽略C
(如程序入口)會有一個B
對象和描述A
和B
類2個System.RuntimeType
對象。
通過C#,第四版參考CLR,P100-110
爲什麼?! 我的答案和Meirion的是一樣的,我提到了「通過C#的CLR」,這是這些作品中被推崇的參考。 –
如果你忽略C作爲程序入口,那麼你將有兩種反射類型,而不是三種。我提出了一個可能使它更清晰的編輯 –
正如我之前所說的那樣,只會有一個對象。和三個類型的對象。 您使用'new'關鍵字創建了b。所以只會有一個對象。如果你讀了我告訴你的書,你會明白的很好 –
對於參數的緣故可以說你做的C
var c = new C();
一個實例在這一點上,你有兩個對象實例,因爲在施工期間C
作出B
的實例。
要回答你的問題,你有一個C
的實例和一個B
的實例。您沒有A
的實例,即使B
源自A
。 (更新:忽略C
任何reflection,你有B
一個對象實例。)
你可以用一些代碼證明了這一點:
class A { }
class B : A { }
class C
{
public B B = new B();
}
class Program
{
static void Main(string[] args)
{
var c = new C();
var b = c.B;
var BasA = (A)b;
bool BisA = BasA.GetType() == typeof (A);
Console.WriteLine($"Assert That B is not A: {!BisA}");
}
}
另外,您可以通過調試器看到所有你的記憶:
另外要小心這些術語。 A
,B
和C
是類別。對象是實例的類。在C#中,描述一個類的信息可以封裝在一個System.Type
類的實例中。
那麼讓我們稍微下去兔子洞吧; 在執行程序集中,有多少對象在內存中?
限制我們的範圍,只有那些班,除了從instanciating C
得到兩個對象,你也將有System.RuntimeType
一個爲A
,B
和C
3個實例:
var assemblyTypes = Assembly.GetExecutingAssembly().GetTypes();
foreach (var classType in assemblyTypes)
Console.WriteLine("Type instance: " + classType);
再次,這是顯示你如何有三個實例System.RuntimeType
,即描述的類A
,B
和C
。
不是學究氣十足,你也將獲得的RuntimeAssembly
(您可執行文件)和RuntimeType
一個實例(您控制檯程序類),以及其他創建
Meirion Hughes:+1獲得詳細的答案。 EXCLUDE用於C類,因爲它將是入口點和任何默認的DotNet/CLR/System.RuntimeType對象。如果我們只考慮A和B,那麼答案是什麼?在你的答案中,你最初說的是「1」,但你的控制檯輸出還顯示了A.的實例。 –
如果你忽略C和其他所有的東西,你有1個對象實例'B',那就是基本答案。在現實和實際的術語中,所有的類都是由'RuntimeType'定義的,因此是內存中的對象。我已經更新了答案以反映這一點。 –
新對象時使用的關鍵字新。
源代碼更像是一個藍圖,其中爲了簡單起見,您指定一個對象擴展行爲和另一個對象的成員(繼承它)。這些信息是描述類型本身所必需的。實際的對象是通過使用關鍵字新的關鍵字基於類型的描述構建的。
在你的情況下,只會創建一個對象。
在你的例子0中,因爲你沒有創建一個C的實例嗎? – BlueTrin
你爲什麼在意?有沒有什麼你做的工作會有所不同,取決於框架內部如何處理?我不完全確定,但我認爲在.NET框架下,如何繼承工作不契約。 – Luaan
@BlueTrin:上面的代碼只是爲了顯示類之間的關係並添加更多信息。開發人員通常比英語更好地理解代碼。無論如何感謝您的快速回復。 –