簡體   English   中英

為什么 .NET 在結構中使用引用類型?

[英]Why .NET uses reference type inside struct?

我正在從事 Blazor 項目,並且正在使用虛擬化組件。 我沒有任何問題,只是想了解一件事。

我發現在Virtualization組件中有ItemsProvider返回ItemsProviderResult<TItem> 它是值類型,但有一個屬性Items ,它是引用類型的集合。

這是我的問題:

  • 如果我們丟失一個 scope 的ItemsProviderResult<TItem>將引用Items留在 memory 中還是將被刪除?
  • 在這種特殊情況下,我如何對Struct vs Class性能差異進行基准測試?
  • 你怎么看,Blazor為什么要這么做?

我用谷歌搜索了有關ValueReference類型的一般信息。 我發現有關該結構的信息建議始終不可變。 但是我還沒有找到問題的答案,如果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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM