Search code examples
c#unsafevalue-type

Get reference of value type C#


Possible Duplicate:
How do I assign by “reference” to a class field in c#?

I want to copy the reference of my value type to another value type. Is it possible without using unsafe paradigm?

int a = 10;
int b = 15;
a = b; // BUT I WANT TO SET REFERENCE OF b !

Here is my real example. I create enum value type and send it as parameters by reference:

   public enum Test
{
    test1,
    test2,
    test3
}

public class MyTestClass1
{
    private Test _test;

    public MyTestClass1(ref Test test)
    {
        _test = test;
    }
}

public class Content
{
    public void Method()
    {
        Test e = Test.test1;
        /*some code*/
        var omyTestClass1 = new MyTestClass1(ref e);
        /*some code*/
        e = Test.test2;
        /*some code*/
        e = Test.test3;
    }
}

Solution

  • What if your method was in fact:

    public MyTestClass1 Method()
    {
        Test e = Test.test1;
        /*some code*/
        var omyTestClass1 = new MyTestClass1(ref e);
        /*some code*/
        e = Test.test2;
        /*some code*/
        e = Test.test3;
        return omyTestClass1;
    }
    

    The value returned contains a reference to a value type that had been on the stack, but now isn't. Now if you try to access that field, you can get anything.

    Worse, what if you write to that reference? If that reference had been stored on the actual call-stack, rather than spending all its time in registers (and we are probably safe in assuming that the very fact that there was a field in a class referring to it meant that it had to be there), well what's there now? It could be a reference to an object. It could be a return address. Writing to it could cause some strange fandango-on-the-core bug, quite possibly some time after the write and hence proving hard to debug.

    We'd lose some of the basic guarantees we have. After you've written to that value, just about any code anywhere can potentially fail in some bizarre way.

    It's worth noting at this point, that C++ and .NET itself (that is to say, everything you can do in .NET including some you can't in C#) which both allow local refs and return values of ref don't allow ref fields.

    The closest you can come is with capturing as happens with lambdas and anonymous methods. Here locals aren't stored on the stack, but in the heap, to allow them to live as long as the lambda they are captured by lives (and get collected when the last of them is collected). You can certainly use this to maintain references to what started as a local, in an object.

    Alternatively, you can use a class that does the necessary work to wrap a value type. Here for example is one I used when I had a similar enough need:

    public sealed class SharedInt
    {
      private int _value;
      /// <summary>Creates a new SharedInt with a value of zero.</summary>
      public SharedInt(){}
      /// <summary>Creates a new SharedInt.</summary>
      /// <param name="value">The initial value of the object.</param>
      public SharedInt(int value)
      {
        _value = value;
      }
      /// <summary>Returns the value of the SharedInt.</summary>
      public int Value
      {
        get { return _value; }
      }
      /// <summary>Returns the value of the SharedInt.</summary>
      /// <param name="ri">The SharedInt to cast.</param>
      /// <returns>An integer of the same value as the SharedInt.</returns>
      public static implicit operator int(SharedInt ri)
      {
        return ri._value;
      }
      /// <summary>Atomically increment the value of the SharedInt by one.</summary>
      /// <returns>The new value.</returns>
      public int Increment()
      {
        return Interlocked.Increment(ref _value);
      }
      /// <summary>Atomically decrement the value of the SharedInt by one.</summary>
      /// <returns>The new value.</returns>
      public int Decrement()
      {
        return Interlocked.Decrement(ref _value);
      }
      /// <summary>Atomically add a value to the SharedInt.</summary>
      /// <param name="addend">The number to add to the SharedInt.</param>
      /// <returns>The new value.</returns>
      public int Add(int addend)
      {
        return Interlocked.Add(ref _value, addend);
      }
      /// <summary>Atomically replace the value of the SharedInt, returning the previous value.</summary>
      /// <param name="value">The number to set the SharedInt to.</param>
      /// <returns>The old value.</returns>
      public int Exchange(int value)
      {
        return Interlocked.Exchange(ref _value, value);
      }
      /// <summary>Atomically subtract a value from the SharedInt.</summary>
      /// <param name="subtrahend">The number to subtract from the SharedInt.</param>
      /// <returns>The new value.</returns>
      public int Subtract(int subtrahend)
      {
        return Interlocked.Add(ref _value, -subtrahend);
      }
    }
    

    I'd a need for some atomicity guarantees that you may not, and likewise maybe didn't need some things that you do need, but it served perfectly well as a means to be able to deal with the same int value from within different classes.

    If you don't mind going through a property each time, you can do a more general-purpose version.

    public class TypedBox<T> where T : struct
    {
      public T Value;
    }
    

    Exposing a field publicly has considerable downsides (which is why we generally never do so), but most of them don't apply to this case (we want to be able to manipulate it fully from outside) and it means that you can even pass the Value as a ref or out parameter.