Search code examples
c#structthread-safetytype-safetyboxing

How to box and unbox a struct with type safety


There is a struct in the .NET platform that I would like to use as state for my class. It is important that the struct is built-in, so I can't change it. Lets say that this built-in struct is the ImmutableStruct below.

struct ImmutableStruct
{
    public string Value { get; }

    public ImmutableStruct(string value) { Value = value; }
}

My class is required to be thread-safe, so the state must be declared volatile. Of course there are other ways to achieve thread-safety, but lets say that a volatile field has been chosen as the best option for the specific case. So I code my class like this:

class MyClass
{
    private volatile ImmutableStruct _state = new ImmutableStruct("SomeString");
    // Error CS0677
    // 'MyClass._state': a volatile field cannot be of the type 'ImmutableStruct'

    public ImmutableStruct State => _state;

    /* Method that mutates the _state is omitted */
}

Unfortunately the compiler does not allow a volatile struct field. So I decide to use boxing and unboxing:

class MyClass
{
    private volatile object _state = new ImmutableStruct("SomeString");

    public ImmutableStruct State => (ImmutableStruct)_state;

    /* Method that mutates the _state reference is omitted */
}

This works, but casting from objects causes me anxiety, because the program becomes exposed to possible run-time errors. I would like a type-safe solution, with the compiler ensuring the correctness of the program at compile-time. So my question is: Is there any way to box and unbox a struct with type safety?

Btw there is a related question about marking structs as volatile: Volatile for structs and collections of structs. My question defers in that it focuses specifically to the type-safety of the boxing/unboxing operation.


Solution

  • How about creating generic wrapper like this:

    public  class ReadonlyStructWrapper<T> where T: struct 
        {
            public T Value { get; }
            public ReadonlyStructWrapper(T value)
            {
                Value = value;
            }
        }