![](/img/trans.png)
[英]Why do C# Arrays use a reference type for Enumeration, but List<T> uses a mutable struct?
[英]Why .NET uses reference type inside struct?
我正在從事 Blazor 項目,並且正在使用虛擬化組件。 我沒有任何問題,只是想了解一件事。
我發現在Virtualization
組件中有ItemsProvider
返回ItemsProviderResult<TItem>
。 它是值類型,但有一個屬性Items
,它是引用類型的集合。
這是我的問題:
ItemsProviderResult<TItem>
將引用Items
留在 memory 中還是將被刪除?Struct vs Class
性能差異進行基准測試? 我用谷歌搜索了有關Value
和Reference
類型的一般信息。 我發現有關該結構的信息建議始終不可變。 但是我還沒有找到問題的答案,如果struct
丟失 scope, Reference
類型 object 會發生什么。
更新1:
我知道引用類型和任何其他引用類型屬性的集合將由 memory 由 GC 釋放,但是它會在 struct 丟失 scope 的同時還是留在 memory 直到 GC 將其刪除?
如果我們丟失一個 scope 的
ItemsProviderResult<TItem>
將引用Items
留在 memory 中還是將被刪除?
在C#,如果有東西從scope出來,最終會從memory釋放出來。
因此對於Items
,當不再有對包含它的結構或Items
本身的引用時,它將有資格進行垃圾回收。 Items
將保留在 memory 中,直到被垃圾收集器釋放。
在這種特殊情況下,我如何對
Struct vs Class
性能差異進行基准測試?
您可以使用 Benchmark.NET 庫來測試使用結構/類執行所需操作的方法。
你怎么看,Blazor為什么要這么做?
Items
將是任何TItem
的集合。
如果TItem
是引用類型,您將擁有一個引用類型的集合。
如果TItem
是值類型,則您將擁有值類型的集合。
值類型將存儲在堆棧中,或者作為引用 object [*] 的一部分存儲在堆中。 由於這種價值類型通常更便宜。 如果它們存儲在堆棧中,它們將由堆棧自動處理。 如果它們是 object 的一部分,它們將作為 object 的一部分被收集,垃圾收集器不需要做任何額外的工作來跟蹤它。
相反,引用類型將始終存儲在堆上。 他們還需要一些額外的 memory 供 GC 用於跟蹤和標記對象。
在某種意義上,值類型只是避免列出多個參數的一種方式。 就垃圾收集器而言,它們可能根本不存在。 持有從 scope 引出的引用的結構的處理方式與從 scope 引出的引用本身的處理方式完全相同。即,當 GC 繞過它時,它將收集引用的 object。 假設沒有其他參考。
一般來說,值類型更適合小型、不可變的類型。 而引用類型更適合較大的類型,或者如果需要更改值。 但是每當您考慮性能時,請考慮:
毫無疑問,效率聖杯會導致濫用。 程序員將大量時間浪費在思考或擔心程序非關鍵部分的速度上,而在考慮調試和維護時,這些提高效率的嘗試實際上會產生強烈的負面影響。 我們應該忘記小效率,大約 97% 的時間說:過早的優化是萬惡之源。
然而,我們不應該放棄那關鍵的 3% 的機會。
也就是說,除非您有成千上萬的對象,否則這可能無關緊要。
庫作者需要更加小心,因為他們不知道他們的庫是如何被使用的。 如果他們意識到自己犯了一個不正確的錯誤,他們就無法真正改變主意,因為這可能會破壞成千上萬用戶的代碼。
[*] 從技術上講,堆棧/堆是實現細節。
至於How could I benchmark Struct vs Class performance differences in this particular case?
.
基准方法如下,想法是模擬虛擬化場景。
[MemoryDiagnoser]
public class GridItemsProviderResultTest
{
record class Model( string Name, string Description );
int dataCount = 5000;
int requestSize = 20;
ICollection<Model> itemsList = new List<Model>();
public DataSourceResultTest()
{
for( int i = 0; i < dataCount; i++ )
{
itemsList.Add( new Model( i + " name " + i, i + " description " + i ) );
}
}
[Fact]
[Benchmark]
public void VirtualizedRows()
{
for( int i = 0; i < dataCount; i += requestSize )
{
GridItemsProviderRequest<Model> request = new( i, requestSize, null, false, default );
IQueryable<Model> data = itemsList.AsQueryable()
.Skip( request.StartIndex )
.Take( request.Count.Value );
GridItemsProviderResult<Model> items = GridItemsProviderResult.From( data.ToArray(), data.Count() );
Assert.True( items.Data.Count == requestSize );
}
}
}
基准測試結果。
GridItemsProviderResult 是結構體,TGridItem 是 class
方法 | 意思 | 錯誤 | 標准偏差 | Gen0 | 第一代 | 已分配 |
---|---|---|---|---|---|---|
虛擬行 | 74.71 毫秒 | 0.181 毫秒 | 0.160 毫秒 | 714.2857 | 285.7143 | 5.96 MB |
GridItemsProviderResult 為 class,TGridItem 為 class
方法 | 意思 | 錯誤 | 標准偏差 | Gen0 | 第一代 | 已分配 |
---|---|---|---|---|---|---|
虛擬行 | 74.63 毫秒 | 0.221 毫秒 | 0.206 毫秒 | 714.2857 | 285.7143 | 5.97 MB |
GridItemsProviderResult 是結構,TGridItem 是結構
方法 | 意思 | 錯誤 | 標准偏差 | Gen0 | 第一代 | 已分配 |
---|---|---|---|---|---|---|
虛擬行 | 71.90 毫秒 | 0.324 毫秒 | 0.303 毫秒 | 714.2857 | 285.7143 | 5.95 MB |
GridItemsProviderResult 為 class,TGridItem 為 struct
方法 | 意思 | 錯誤 | 標准偏差 | Gen0 | 第一代 | 已分配 |
---|---|---|---|---|---|---|
虛擬行 | 71.83 毫秒 | 0.400 毫秒 | 0.354 毫秒 | 714.2857 | 285.7143 | 5.96 MB |
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.