简体   繁体   English

为什么 .NET 在结构中使用引用类型?

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

I am working on Blazor project, and I am using Virtualization component.我正在从事 Blazor 项目,并且正在使用虚拟化组件。 I don't have any issue, but just trying to understand one thing.我没有任何问题,只是想了解一件事。

I found that in Virtualization component there is ItemsProvider which returns ItemsProviderResult<TItem> .我发现在Virtualization组件中有ItemsProvider返回ItemsProviderResult<TItem> It is Value type, but has a property Items which is collection of reference types.它是值类型,但有一个属性Items ,它是引用类型的集合。

And here are my questions:这是我的问题:

  • If we lose a scope of ItemsProviderResult<TItem> will reference to Items be left in memory or it will be removed?如果我们丢失一个 scope 的ItemsProviderResult<TItem>将引用Items留在 memory 中还是将被删除?
  • How could I benchmark Struct vs Class performance differences in this particular case?在这种特殊情况下,我如何对Struct vs Class性能差异进行基准测试?
  • What do you think, why Blazor has done so?你怎么看,Blazor为什么要这么做?

I have googled general information about Value and Reference types.我用谷歌搜索了有关ValueReference类型的一般信息。 I found information about that struct is recommended to be always immutable.我发现有关该结构的信息建议始终不可变。 But I haven't found an answer for question what happens with Reference type object if struct loses scope.但是我还没有找到问题的答案,如果struct丢失 scope, Reference类型 object 会发生什么。

UPD1:更新1:

I understand that the collection of reference type and any other reference type property will be released from memory by GC, but will it be at the same time when struct lose scope or it will be staying in memory until GC will remove it?我知道引用类型和任何其他引用类型属性的集合将由 memory 由 GC 释放,但是它会在 struct 丢失 scope 的同时还是留在 memory 直到 GC 将其删除?

If we lose a scope of ItemsProviderResult<TItem> will reference to Items be left in memory or it will be removed?如果我们丢失一个 scope 的ItemsProviderResult<TItem>将引用Items留在 memory 中还是将被删除?

In C#, if something goes out of scope it will eventually be released from memory.在C#,如果有东西从scope出来,最终会从memory释放出来。

So for Items , it will become eligible for garbage collection when there are no more references to the struct that contains it or Items itself.因此对于Items ,当不再有对包含它的结构或Items本身的引用时,它将有资格进行垃圾回收。 Items will remain in memory until released by the garbage collector. Items将保留在 memory 中,直到被垃圾收集器释放。

How could I benchmark Struct vs Class performance differences in this particular case?在这种特殊情况下,我如何对Struct vs Class性能差异进行基准测试?

You can make use of the Benchmark.NET library for testing methods that perform the desired operations using structs/classes.您可以使用 Benchmark.NET 库来测试使用结构/类执行所需操作的方法。

What do you think, why Blazor has done so?你怎么看,Blazor为什么要这么做?

Items will be a collection of whatever TItem is. Items将是任何TItem的集合。

If TItem is a reference type, the you will have a collection of reference types.如果TItem是引用类型,您将拥有一个引用类型的集合。

If TItem is a value type, the you will have a collection of value types.如果TItem是值类型,则您将拥有值类型的集合。

Value types will either be stored in the stack, or on the heap as part of an reference object [*].值类型将存储在堆栈中,或者作为引用 object [*] 的一部分存储在堆中。 Because of this value types are typically cheaper.由于这种价值类型通常更便宜。 If they are stored on the stack they will be handled automatically by the stack.如果它们存储在堆栈中,它们将由堆栈自动处理。 If they are part on an object they will be collected as part of that object, the garbage collector does not need to do any extra work to keep track of it.如果它们是 object 的一部分,它们将作为 object 的一部分被收集,垃圾收集器不需要做任何额外的工作来跟踪它。

In contrast, reference types will always be stored on the heap.相反,引用类型将始终存储在堆上。 They will also need some extra memory for the GC to use for tracking and marking objects.他们还需要一些额外的 memory 供 GC 用于跟踪和标记对象。

In some sense, value types is just a way to avoid listing multiple parameters.在某种意义上,值类型只是避免列出多个参数的一种方式。 They might as well not exist at all as far as the garbage collector is concerned.就垃圾收集器而言,它们可能根本不存在。 A struct holding a reference going out of scope will be handled exactly the same as the reference itself going out of scope. Ie the GC will collect the referenced object when it gets around to it.持有从 scope 引出的引用的结构的处理方式与从 scope 引出的引用本身的处理方式完全相同。即,当 GC 绕过它时,它将收集引用的 object。 Assuming there are no other references.假设没有其他参考。

In general, value types are preferred for small, immutable, types.一般来说,值类型更适合小型、不可变的类型。 While reference types are preferred for larger types, or if mutation of the value is needed.而引用类型更适合较大的类型,或者如果需要更改值。 But whenever you think about performance, consider :但是每当您考虑性能时,请考虑

There is no doubt that the grail of efficiency leads to abuse.毫无疑问,效率圣杯会导致滥用。 Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered.程序员将大量时间浪费在思考或担心程序非关键部分的速度上,而在考虑调试和维护时,这些提高效率的尝试实际上会产生强烈的负面影响。 We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.我们应该忘记小效率,大约 97% 的时间说:过早的优化是万恶之源。

Yet we should not pass up our opportunities in that critical 3%.然而,我们不应该放弃那关键的 3% 的机会。

Ie it probably does not matter unless you have many thousands of objects.也就是说,除非您有成千上万的对象,否则这可能无关紧要。

Library authors need to be more careful, since they do not know how their library is used.库作者需要更加小心,因为他们不知道他们的库是如何被使用的。 And they cannot really change their mind if they realize they made a incorrect mistake, since that might break the code for thousands of users.如果他们意识到自己犯了一个不正确的错误,他们就无法真正改变主意,因为这可能会破坏成千上万用户的代码。

[*] Technically, stack/heap are implementation details. [*] 从技术上讲,堆栈/堆是实现细节。

As for the How could I benchmark Struct vs Class performance differences in this particular case?至于How could I benchmark Struct vs Class performance differences in this particular case? . .

Benchmark methodology is below, idea is to simulate Virtualization scenario.基准方法如下,想法是模拟虚拟化场景。

[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 );
        }
    }
}

Benchmark results.基准测试结果。

GridItemsProviderResult is struct, TGridItem is class GridItemsProviderResult 是结构体,TGridItem 是 class

Method方法 Mean意思 Error错误 StdDev标准偏差 Gen0 Gen0 Gen1第一代 Allocated已分配
VirtualizedRows虚拟行 74.71 ms 74.71 毫秒 0.181 ms 0.181 毫秒 0.160 ms 0.160 毫秒 714.2857 714.2857 285.7143 285.7143 5.96 MB 5.96 MB

GridItemsProviderResult is class, TGridItem is class GridItemsProviderResult 为 class,TGridItem 为 class

Method方法 Mean意思 Error错误 StdDev标准偏差 Gen0 Gen0 Gen1第一代 Allocated已分配
VirtualizedRows虚拟行 74.63 ms 74.63 毫秒 0.221 ms 0.221 毫秒 0.206 ms 0.206 毫秒 714.2857 714.2857 285.7143 285.7143 5.97 MB 5.97 MB

GridItemsProviderResult is struct, TGridItem is struct GridItemsProviderResult 是结构,TGridItem 是结构

Method方法 Mean意思 Error错误 StdDev标准偏差 Gen0 Gen0 Gen1第一代 Allocated已分配
VirtualizedRows虚拟行 71.90 ms 71.90 毫秒 0.324 ms 0.324 毫秒 0.303 ms 0.303 毫秒 714.2857 714.2857 285.7143 285.7143 5.95 MB 5.95 MB

GridItemsProviderResult is class, TGridItem is struct GridItemsProviderResult 为 class,TGridItem 为 struct

Method方法 Mean意思 Error错误 StdDev标准偏差 Gen0 Gen0 Gen1第一代 Allocated已分配
VirtualizedRows虚拟行 71.83 ms 71.83 毫秒 0.400 ms 0.400 毫秒 0.354 ms 0.354 毫秒 714.2857 714.2857 285.7143 285.7143 5.96 MB 5.96 MB

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM