For some general helper methods I'm writing, I'd like to be able to invoke special processing when that value is the default value of its type. For reference types, that's easy - the default value is null
. I can't use a generic type parameter, although I could work around that.
I can do something like this:
public bool DetectPossiblyUninitializedValue(object val) {
return val== null ||
val.GetType().IsValueType
&& Equals(val, Activator.CreateInstance(val.GetType());
}
That's what I'm using right now, but it depends on the implementation of Equals
. That's fine, but not ideal. In particular, some implementations might override Equals to support more usable semantics in normal scenarios. It's actually not uncommon to treat the default value as special here because it's so unavoidable in .NET due to default initialization.
However, in this case, I just want to know whether the object may have been initialized, and I therefore don't want any custom equality or whatever. Basically, I want to know whether the memory region the struct occupies is filled with zero's as the VM guarrantees after initialization, and no more. In a sense I'm looking for something akin to ReferenceEquals
for structs: a comparison disregarding the underlying object's own implementation.
How can I compare raw struct values without using Equals
? Can I compare raw struct values at all?
Edit: I'm using this to hookup classes+structs representing domain-specific notions connected by essentially arbitrary code representing various business rules to a GUI. Some old code essentially deals with possibly nested dictionaries of string to arbitrary objects, which thus requires a bunch of unchecked casts or dynamic
; creating these is error prone. So it's nice to be able to work with the typed objects relatively directly. On the other hand, it's useful for the GUI and wrapping code to treat possibly uninitialized values differently; and although a case-by-case, type-by-type solution is possible, that's lots of code; a sensible default is useful. Really what I want is a method of automatically generating a type that's identical to another but with all properties/public fields extended to include a value "uninitialized", but that's not a realistic feature to expect - by contrast in a dynamic world this would be trivially achievable, though without typesafety elsewhere...
Answers: Mehrdad posted an answer on how to directly access the bits of structs; I added an implementation using that to detect possibly uninitialized values.
For variables of class types and other reference types, this default value is null . However, since structs are value types that cannot be null , the default value of a struct is the value produced by setting all value type fields to their default value and all reference type fields to null .
Default values can be assigned to a struct by using a constructor function. Rather than creating a structure directly, we can use a constructor to assign custom default values to all or some of its members. Another way of assigning default values to structs is by using tags.
A struct is a value type, so it's always passed as a value. A value can either be a reference type (object) or a value type (struct).
Yes they can. It depends. Many hold the stance that a struct should be immutable, and in this case, holding a reference to an object could mean it isn't. But it depends on the situation.
If you're worried about the overhead of boxing (and you've measured that this is a bottleneck), you can solve it differently:
Create two temporary boxed instances of your struct as an object
, which can be reused for all structs. Using Reflection.Emit
, create a method that uses the Unbox
opcode to copy a struct to a the boxed version. (This lets you avoid an allocation.) Do the same thing with the other boxed struct, then call Equals
on the objects.
I don't know if the overhead of a delegate call is actually faster, but you could try anyway and see. If you find out it's not, then you could always do more than one comparison at once -- pass in an array or something. It gets complicated, but if you know this is the bottleneck then it might be worth it, depending on how big your struct
s are.
I'm not supporting this solution, merely suggesting that it exists. If you don't know what this is doing, don't use it.
bool UnsafeHackyEquals<T>(ref T a, ref T b) where T : struct
{
TypedReference pA = __makeref(a), pB = __makeref(b);
var size = SizeOf<T>();
IntPtr* ppA = (IntPtr*)&pA, ppB = (IntPtr*)&pB;
//Now ppA[0] is a pointer to a, and ppB[0] is a pointer to b.
//You have the size of both, so you can do a bitwise comparison.
}
To find the size of a struct:
static class ArrayOfTwoElements<T> { static readonly T[] Value = new T[2]; }
static uint SizeOf<T>()
{
unsafe
{
TypedReference
elem1 = __makeref(ArrayOfTwoElements<T>.Value[0] ),
elem2 = __makeref(ArrayOfTwoElements<T>.Value[1] );
unsafe
{ return (uint)((byte*)*(IntPtr*)(&elem2) - (byte*)*(IntPtr*)(&elem1)); }
}
}
Yes, it'd kind of undocumented. But if you're worried about that, you could just emit this method instead (because the MkRefAny
opcode is indeed documented), so that's not an issue. However, this example can break on other platforms, so be careful...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With