使用" union struct"避免使用cast / box / unbox

时间:2016-03-12 01:16:55

标签: c# jit cil

在某种情况下,我需要管理受约束的值。简化;我只想说我需要将值约束为字符串或64位整数。

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

在这种简化的情况下,类型字段也许可以省略,因为我们可以通过它们的CLR类型来区分字符串和整数。然而;我需要将type字段用于其他目的(多个"约束值类型"可以用单个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说明:

  • String-getter需要castclass指令才能从对象转换为字符串。
  • Int64-getter需要unbox.any指令才能从对象转换为long。
  • Int64-setter需要box指令才能从long转换为object。

这个结构的目的是强制执行约束,所以当它获取或设置一个值时,它就知道它是正确的类型。

因此,我考虑使用FieldOffset属性。像这样:

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

使用此方法,没有额外的box,unbox或cast指令。这让我觉得这种方法更好。

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

也许有JIT编译器出于某种原因会阻塞它?

在实际代码中;结构将是不可变的。这些字段是只读的,不会有任何setter。

首先,我并不认为这会产生影响,因为它或多或少意味着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编译器是否足够聪明,可以忽视这些浪费的指示?

1 个答案:

答案 0 :(得分:1)

没关系。似乎无论如何都无法做到这一点。尝试加载此类型会导致TypeLoadException说出类似

的内容
  

无法从程序集“MyAssembly”加载“MyValue”类型,因为它在偏移0处包含一个对象字段,该字段未正确对齐或与非对象字段重叠

所以,我想不可能在与值类型字段相同的偏移量上有一个引用类型字段。

故事结束。

我要离开这个Q& A(不删除),以防万一其他人好奇。