0

Currently, I have this code:

class SO
{
    public SO()
    {
        var ptrManager = new PointerManager();
        int i = 1;

        ptrManager.SavePointer(ref i);

        Console.WriteLine(i); // prints 1

        i = 2; //change value to 2 within the current scope
        Console.WriteLine(i); // prints 2

        ptrManager.SetValue(3); // change value to 3 in another (unsafe) scope
        Console.WriteLine(i); // prints 3
    }
}

unsafe class PointerManager
{
    private int* ptr;
    public void SavePointer(ref int i)
    {
        fixed (int* p = &i)
        {
            ptr = p;
        }
    }

    public void SetValue(int i)
    {
        *ptr = i;
    }
}

First, I tell the PointerManager to create a pointer pointing to my int i. This way, later, i'll be able to tell the PointerManager to change the value of i, without having to pass i in again.

This seems to work well so far. However, I've read that if the GC decides to move i around in memory, then my pointer ptr will become invalid, and subsequent calls to SetValue will have unpredictable behaviour.

So, how do I prevent GC from moving i around? Or, alternatively, is there a better way of accomplishing my goal?

I've also been told that simply adding GCHandle.Alloc(i, GCHandleType.Pinned); will work, but that doesn't seem right...

Edit: To be more specific, GCHandle.Alloc would be called at the beginning of the SavePointer method.

dcastro
  • 66,540
  • 21
  • 145
  • 155
  • 1
    Maybe you can describe what problem you are trying to solve? Because it doesn't seem overly useful at first glance. – nvoigt Sep 13 '13 at 10:55
  • I'm trying to implement transactional objects. You'd register your variables within the "Manager", do some work, and at a later point, if anything goes wrong, you'd just call `manager.Rollback`, restoring the values of your variables. – dcastro Sep 13 '13 at 10:57

2 Answers2

1

You can use the power of Function and Action types to achieve the same without any unsafe code. Try to avoid any unsafe ocde or pointers, if you aren't interfacing with non-managed code, you don't need them:

using System;

namespace ConsoleApplication16
{
  class SO
  {
    public static void Main()
    {
      var manager = new LateSetter<int>();

      int i = 1;

      manager.SaveSetter(newValue => i = newValue);

      Console.WriteLine(i); // prints 1

      i = 2;
      Console.WriteLine(i); // prints 2

      manager.SetValue(3); // change value to 3
      Console.WriteLine(i); // prints 3

      Console.ReadLine();
    }
  }

  class LateSetter<T>
  {
    private Action<T> set;

    public void SaveSetter(Action<T> setter)
    {
      this.set = setter;
    }

    public void SetValue(T i)
    {
      this.set(i);
    }
  }
}
nvoigt
  • 75,013
  • 26
  • 93
  • 142
  • Why didn't I think of that?! I'm using the same strategy for other things all over the project! Geez.. One more question though, if the manager receives an Expression with the setter... do you think it is possible to derive the getter? Because I'd have to first _get_ the current value of the closured variable, so I can later _set_ the original value. – dcastro Sep 13 '13 at 11:15
  • In other words, I'll need both a getter and a setter, and I wouldn't want to force the user to provide both... I should be able to build the expression tree for a getter if I have a setter, right? (or the other way around) – dcastro Sep 13 '13 at 11:17
1

Generally you do not want to prevent the GC from moving around anything - especially in your case (transactional objects), where I would rather consider options like:

  • making objects savable+restorable=serializable
  • doing it with some sort of manager (maybe this fits well to the idea you had with pointers) that you can pass closures!

Second option explained:

class VariableManager
{
    private Action<int> setter;
    public void VariableManager(Action<int> setter)
    {
        this.setter = setter;
    }

    public void SetValue(int i)
    {
        setter(i);
    }
}

Simply create the manager via new VariableManager(i => yourInt = i) and viola - there you have your desired semantics without pointers!

olydis
  • 3,192
  • 13
  • 28
  • Great idea, thanks! Also, can you give me a couple of links about "making objects savable+restorable=serializable"? I'll probably go with closures, but I'd still like to investigate as many alternatives as possible. – dcastro Sep 13 '13 at 11:21
  • Glad to help, this MSDN page is a neat starting point to learn about serialization: http://msdn.microsoft.com/en-us/library/vstudio/ms233843.aspx – olydis Sep 13 '13 at 11:24
  • Oh, so that's what you meant? I though you meant something else... See, unless the objects sent to the manager know how to restore themselves, then how would the manager restore them? – dcastro Sep 13 '13 at 11:31
  • I can save a copy of the object like this: `var serialized = Converter.Serialize(obj)`, but then how would I restore them at a later point (without using a setter)? Or did you mean that I should make the objects serializable AND use setters? – dcastro Sep 13 '13 at 11:32
  • absolutely, serialization (although it is also possible to deserialize INTO existing objects) does not seem to fit your needs that well - I guess I would work with "managers" with setters as well... – olydis Sep 13 '13 at 11:48