是一个带有字符串字段(或字节数组)(非值类型)的.net结构,可以节省内存

本文关键字:net 类型 结构 内存 节省 字节数 一个 字符串 字节 字段 数组 | 更新日期: 2023-09-27 18:11:17

前几天(实际上是很久很久以前),我正在阅读优秀的CLR via c#书籍(第4版),我强烈推荐给任何正在做c#开发的人,以更好地理解底层机制并解释幕后的魔力。

从那篇文章开始,我开始过度担心在设计决策中是否应该使用结构体而不是类。

我有一种情况,可以完美地使用结构体(临时数据块(又名结构体)的快速通信)。

然而,我仍然在这个场景中使用结构体,因为我已经看到了这么多的经验法则。例如,实例必须小于16个字节,或者我愿意携带一个数组或字符串(至少是不可变的)作为结构字段之一,而字符串需要稍后以某种方式被垃圾收集,等等。

这样看来,struct的使用就不仅仅是有限了。

下面是一个过度简化的c# 6的例子,我想关于内存效率的答案是:不,请不要使用struct,因为你最终会浪费很多内存,传递它作为参数将会非常贪婪,性能明智。

public enum ChunkHeader : ushort
{
    Unknown = 0x00,
    Transmitted = 0x01,
    Received = 0x02,
}
public struct Chunk
{
    public static Chunk Empty = new Chunk(ChunkHeader.Unknown, new Byte[0]);
    public Chunk(ChunkHeader header, Byte[] data)
    {
        this.Header = header;
        this.Data = ExceptionHelpers.ThrowIfNull(data, nameof(data))
    }
    // C# 6 read-only >>auto<<-properties        
    public ChunkHeader Header { get; }
    public Byte[] Data { get; }
}
public static class ExceptionHelpers
{
    public static T ThrowIfNull<T>(T parameterValue, String parameterName)
    {
        if ((Object)parameterValue == null)
        {
            throw new ArgumentNullException(parameterName);
        }
        else
        {
            return parameterValue;
        }
    }
}

我的问题很简单,你认为上面的结构在垃圾收集方面是内存高效的吗?还是使用考虑结构中底层引用类型字段的类定义更好?

如果是这样,有什么解决方法或通用准则来处理大量数据块,以防止大规模的垃圾收集?

请考虑这些数据块只打算在短时间内使用。

是一个带有字符串字段(或字节数组)(非值类型)的.net结构,可以节省内存

这是你想读的文章。这就是所谓的"在类和结构之间选择"。

你知道,我认为在你给出的例子中创建结构体是有意义的。您的Chunk似乎表示单个值,类似于基本类型(int, double等)。

正如文章本身所说,有三个重要的方面要记住-

  1. 分配:你的struct将在栈上分配,而类将在堆上分配;
  2. Boxing/Unboxing:如果你要让Chunk实现一些接口,那么这个值类型将被装箱/拆箱,这将对性能产生负面影响。引用类型不是这样;
  3. 值与引用:结构数据将作为值传递,而类的对象将作为引用传递。因此,对传递的结构体所做的更改不会反映在所有其他副本中,这与引用类型不同。

总结:

类的实例

√考虑定义一个结构体而不是类类型较小且通常寿命较短或通常嵌入其他对象。

X避免定义结构体,除非该类型具有所有的具有以下特征:逻辑上表示单个值;类似于基本类型(int、double等)。它有一个实例大小小于16字节。它是不可变的。它不需要装箱频繁。


关于避免使用结构体的两点思考

16字节大小指南-(答案来自这里): 16字节指南只是一个性能经验法则。关键是,因为值类型是按值传递的,所以如果传递给函数,就必须复制结构体的整个大小,而对于引用类型,只需要复制引用(4字节)。结构体可能会节省一些时间,因为您删除了间接层,所以即使它大于这4个字节,它仍然可能比传递引用更有效。但在某些时候,它变得如此之大,以至于复制的成本变得显而易见。一个常见的经验法则是,这通常发生在16字节左右。

不变性: 不变性意味着无论你在结构体中放入什么值,你只会设置它们一次,并且它们将是final。例如,如果您为块指定了一个名称,那么该名称在其生命周期内将是永久的。如果块在其整个生命周期中将有许多名称,那么最好创建一个类。对数据应用相同的逻辑来决定结构还是类。

在所有其他情况下,您应该将类型定义为类。