使用结构体作为原始数据类型的包装器以进行类型检查会产生额外开销吗?

21

假如我想获取额外的类型检查,以便处理在语义上具有不同含义的基本数据类型:

public struct Apple
{
    readonly int value;

    // Add constructor + operator overloads
}

public struct Orange
{
    readonly int value;

    // Add constructor + operator overloads
}

要点是我们不能将“苹果”与“橙子”进行比较,因此将实际整数封装在结构体中意味着我们获得了类型检查、一些额外的可读性和代码文档。

我的问题是:这样做会带来什么开销,无论是在内存还是速度方面?由于结构体是值类型,包含这些结构体的变量是否为32位或更大?使用这些结构体而不是基元的性能开销如何——操作符重载是否存在较大的开销?

对于这样做的智慧,您有什么其他建议吗?


4
不涉及性能问题,但我最近采用类似的方法时遇到了麻烦。结构体强制你拥有一个默认的空构造函数,而你无法控制它,这意味着你无法防止创建一个零苹果的情况。 - Mathias
在我的特定情况下,0 值是可以接受的,但总体来说需要注意。虽然对于整数也可以做同样的事情,所以这并不是与原始类型相比的缺点,而是需要记住的限制。 - Mike
默认构造函数的问题通常可以通过使用私有成员存储值和属性,并在getter中使用逻辑(而不是setter)来检查default(T)并将其替换为所需的值来缓解。具有“错误”默认值的布尔标志可以通过将内部成员存储为接口含义的相反来缓解。但我同意,在C#中这很麻烦,实际上不应该出现这种情况。 - richardtallent
点赞因为我也想知道答案,还有“不能拿苹果和橙子比较”的问题 :) 你有没有了解性能开销?我的希望是开销都是在编译时消失的语法糖。 - yoyo
2个回答

9

在内存中使用结构体没有额外开销,你可以通过 Marshal.SizeOf() 进行验证:

struct testStruct
{
    public readonly int value;
}
..

int size = Marshal.SizeOf(typeof(testStruct)); //returns 4

这也与sizeof(testStruct)返回的相同:
unsafe
{
    int size = sizeof(testStruct); //returns 4
}

根据MSDN,这两种尺寸方法的区别在于:
虽然您可以使用Marshal.SizeOf方法,但此方法返回的值并不总是与sizeof返回的值相同。Marshal.SizeOf返回类型进行编组后的大小,而sizeof返回由公共语言运行时分配的大小,包括任何填充。

很好知道这一点。我曾经想过是否是这样的。谢谢你提供有关确定对象大小的提示。我不知道这一点。 - Mike

2

我可能因此而受到排斥,但您可以这样做:

using Apple = System.Int32;
using Orange = System.Int32;

您无法在不同文件之间使用它。技术上,您仍然可以将苹果与橙子进行比较。


有助于(稍微)提高可读性,但并不能满足原帖作者对类型检查的需求。 - yoyo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接