使用"union struct"避免投掷/装箱/拆箱



在某种情况下,我需要管理约束值。为了简化;假设我需要将值约束为字符串或 64 位整数。

为此;我正在考虑声明一种结构类型,该结构类型有一个字段用于存储的值类型,一个字段用于实际值。

在这种简化的情况下,类型字段也许可以省略,因为我们可以通过 CLR 类型来区分字符串和整数。然而;我需要类型字段用于其他目的(多个"受约束值类型"可以由单个 CLR 类型表示)。

一个直接的方法:

public struct MyValue
{
    private object _value;
    private MyValueType _type;
    public string String
    {
        get
        {
            // todo: check type
            return (string)_value;
        }
        set
        {
            // todo: validate value
            _type = MyValueType.String;
            _value = value;
        }
    }
    public long Int64
    {
        get
        {
            // todo: check type
            return (long)_value;
        }
        set
        {
            // todo: validate value
            _type = MyValueType.Int64;
            _value = value;
        }
    }
}

但是,此方法需要一些"额外的"IL 指令:

  • 字符串获取器需要一个castclass指令才能从对象转换为字符串。
  • Int64-getter 需要一个unbox.any指令才能从对象转换为 long。
  • Int64 setter 需要一条box指令才能从 long 转换为对象。

此结构的目的是强制实施约束,因此在获取或设置值时,已知它将属于正确的类型。

因此,我正在考虑使用字段偏移属性。像这样:

[StructLayout(LayoutKind.Explicit)]
public struct MyValue
{
    [FieldOffset(0)]
    private string _string;
    [FieldOffset(0)]
    private long _int64;
    [FieldOffset(8)]
    private MyValueType _type;
    public string String
    {
        get
        {
            // todo: check type
            return _string;
        }
        set
        {
            // todo: validate value
            _type = MyValueType.String;
            _string = value;
        }
    }
    public long Int64
    {
        get
        {
            // todo: check type
            return _int64;
        }
        set
        {
            // todo: validate value
            _type = MyValueType.Int64;
            _int64 = value;
        }
    }
}

使用这种方法,没有额外的盒子、拆箱或铸造说明。这让我觉得这种方法更好。

问题是:使用显式结构布局和字段偏移属性有什么缺点吗?

也许JIT编译器会因为某种原因而窒息?


在实际代码中;结构是不可变的。这些字段将是只读的,并且没有任何资源库。

首先,我认为这不会有什么不同,因为它或多或少意味着 setter 将移动到构造函数中,每个值类型一个。

但是;编译器要求所有结构成员由构造函数初始化 - 而不考虑它们具有相同的字段偏移量。

我需要做这样的事情:

public MyValue(string value)
{
    // todo: validate value
    _int64 = 0; // just to satisfy the compiler
    _string = value;
    _type = MyValueType.String;
}
public MyValue(long value)
{
    // todo: validate value
    _string = null; // just to satisfy the compiler
    _int64 = value;
    _type = MyValueType.Int64;
}

这意味着第二种方法也需要"额外的"IL指令。有三个额外的说明用于"默认"每个不会使用的字段。

例如:设置_string = null产量ldarg.0ldnullstfld

这些额外的说明完全是浪费。如果我添加其他字段,情况会变得更糟。

那么,问题也是:JIT编译器是否足够聪明,可以忽略这些浪费的指令?

关系。看来反正这做不到。尝试加载这样的类型会导致TypeLoadException说类似

无法从程序集"MyAssembly"加载类型"MyValue",因为它包含偏移量为 0 的对象字段,该字段未正确对齐或与非对象字段重叠

因此,我想不可能在与值类型字段相同的偏移量上具有引用类型字段。

故事结束。

我将离开这个问答(不删除),以防其他人好奇。

相关内容

  • 没有找到相关文章

最新更新