C# 带有字符串字段(或字节数组)(非值类型)的.NET结构是否可以节省内存?

C# 带有字符串字段(或字节数组)(非值类型)的.NET结构是否可以节省内存?,c#,.net,arrays,string,struct,C#,.net,Arrays,String,Struct,前几天(比如说很久以前),我正在通过C#book(第四版)阅读优秀的CLR,我强烈推荐给任何正在进行C#开发的人,以更好地理解潜在的机制并解释幕后的魔力 从那次阅读开始,我开始过分担心在我的设计决策中是否应该使用结构而不是类 我有一种情况可以完美地使用结构(临时数据块(又称结构)的快速通信) 然而,我仍然在努力在这个场景中使用结构,因为我已经看到了很多经验法则。例如,实例必须小于16字节,或者我愿意携带一个数组或字符串(至少是不可变的)作为结构字段之一,而字符串稍后需要以某种方式进行垃圾收集,等

前几天(比如说很久以前),我正在通过C#book(第四版)阅读优秀的CLR,我强烈推荐给任何正在进行C#开发的人,以更好地理解潜在的机制并解释幕后的魔力

从那次阅读开始,我开始过分担心在我的设计决策中是否应该使用结构而不是类

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

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

看来结构的使用是非常有限的

下面是一个过于简化的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;
        }
    }
}
public enum ChunkHeader:ushort
{
未知=0x00,
已传输=0x01,
已接收=0x02,
}
公共结构块
{
public static Chunk Empty=新块(ChunkHeader.Unknown,新字节[0]);
公共块(ChunkHeader头,字节[]数据)
{
this.Header=Header;
this.Data=ExceptionHelpers.ThrowIfNull(数据,名称(数据))
}

//C#6只读>>自动这是你想读的文章。它的名字正是

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

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

  • 分配:您的结构将在堆栈上分配,而类将在堆上分配
  • 装箱/取消装箱:如果要使
    Chunk
    实现某些接口,则此值类型将装箱/取消装箱,这将对性能产生负面影响。引用类型不是这种情况
  • 值与引用:结构数据将作为值传递,而类对象将作为引用传递。因此,与引用类型不同,对传递的结构所做的更改不会反映在所有其他副本中
  • 总结如下:

    √ 如果是实例,请考虑定义结构而不是类。 类型很小,通常寿命很短,或者通常嵌入到 其他物体

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


    关于避免结构的两点 16字节大小准则-(答案取自):16字节准则只是一条性能经验法则。要点是,由于值类型是按值传递的,因此如果将结构传递给函数,则必须复制结构的整个大小,而对于引用类型,仅复制引用(4字节)必须复制。结构可能会节省一些时间,因为您删除了一层间接寻址,因此即使它大于这4个字节,也可能比传递引用更有效。但在某些情况下,它变得如此之大,以至于复制成本变得显而易见。一个常见的经验法则是,这通常发生在大约16个字节

    不变性:不变性的方面意味着,无论您在结构中输入什么值,您只需设置一次,它们将是最终值。例如,如果您为块命名,则该名称将在其生命周期内保持不变。如果该块在其生命周期内有多个名称,您最好创建一个对数据应用相同的逻辑来决定结构与类


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

    吹毛求疵,但您不能为C#中的结构定义无参数构造函数。您在这里使用的功能尚未成为官方语言。您的问题是什么?是否与“对于许多短寿命字符串,不必经常等待垃圾收集,使用什么样的数据结构"?@DStanley否,但只读自动属性是。请注意,没有setter和明确的支持字段。是的,它也包含在Visual Studio的一些候选版本中,但最终该功能从编译器中收回。我建议您更新工具链,这样您就不会意外地得到恼人的不可编译代码。您是否考虑过将块
    数据
    数组合二为一?如果您认为垃圾收集是一个问题,那么这是避免垃圾收集的直接方法。(我认为包含
    数据
    的对象是
    结构还是
    都不重要——它都包含指向字节数组的指针。)感谢displayName为您提供的答案,我已经阅读了msdn;-)总体1-分配:线程堆栈,这很好2-装箱/拆箱:到目前为止没有接口,但我知道装箱行为。CLR via C#一书用大量示例很好地解释了这一点3-值与参考:在我的情况下我也只是对16字节的东西持怀疑态度。让我们考虑一下,我的结构嵌入了一个字符串,而不是一个字节数组。