在特定的上下文中,我需要管理约束值。简化;假设我需要将值限制为字符串或 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 指令:
- 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;
}
}
}
使用这种方法,没有额外的装箱、拆箱或转换说明。这让我觉得这种方法更好。
问题是:使用显式结构布局和字段偏移属性有什么缺点吗?
也许 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.0
、ldnull
和stfld
。
这些额外的指令完全是浪费。如果我添加额外的字段,情况会变得更糟。
所以;还有一个问题是:JIT 编译器是否足够聪明以忽略这些浪费的指令?
最佳答案
没关系。看来这无论如何也做不到。尝试加载此类类型将导致 TypeLoadException
类似
Could not load type 'MyValue' from assembly 'MyAssembly' because it contains an object field at offset 0 that is incorrectly aligned or overlapped by a non-object field.
所以,我猜想引用类型字段不可能与值类型字段位于相同的偏移量上。
故事结束。
我将离开这个问答(而不是删除),以防其他人好奇。
关于c# - 使用 "union struct"来避免 cast/box/unbox,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35952571/