3

Say I have a simple class like this:

public class ReferenceChanger<T> 
{
    public T SavedElement { get; private set; }

    public ReferenceChanger(T elementToSave)
    {
        SavedElement = elementToSave;
    }

    // This method would change the original variable (given as a constructor argument)
    public void SetNewReference(T newElement)
    {
        SavedElement = newElement;
    }
}

This class saves an element given to its constructor, whatever element is. However, the "SavedElement" (its backing field) is a reference to the object given at the time of instance creation.

Is there any way to save a reference to a variable (as with using ref keyword), so that if the original item passed to the constructor changes, the SavedElement would automatically reflect the change, almost as if the object was passed with the ref keyword? (Even if I use the ref keyword, I would not be able to save the reference that way.)

Updated to make intentions more clear:

public class ExampleClass 
{
    public List<int> Numbers { get; set; }
}

public static void Main()
{
    ExampleClass temp = new ExampleClass();
    temp.Numbers = new List<int>() { 1, 2, 3 }; 

    ReferenceChanger<List<int>> changer = new ReferenceChanger<List<int>>(temp.Numbers);
    // Here, a reference to the List<int> instance (containing 1,2,3) is stored in changer's SavedElement

    // Then I do this:
    changer.SetNewReference(new List<int>() { 5, 6, 7 });

    // Now, only changer's SavedElement was changed, but temp's property Numbers was not changed.
    // Is there a way to change the temp's property Numbers from the changer?
}
Kornelije Petak
  • 9,412
  • 15
  • 68
  • 96
  • 1
    seeing that classes are automatically reference types, in change in the object passed to your ctor should be reflected in your SavedElement, as it's merely referencing that object, it's not being passed by value, but by reference. – Tony The Lion Sep 06 '11 at 21:06
  • Do you mean to say that you want code that consumed the value of `SavedElement` before the call to `SetNewReference` to subsequently carry out operations on the new version of `SavedElement`? – Reddog Sep 06 '11 at 21:12
  • If so, just pass the whole instance of ReferenceChanger to the operation and have it call SavedElement when it needs to. It'll always use the "latest" reference. – Reddog Sep 06 '11 at 21:12
  • @Tony I think you have misunderstood my question. I will add some more code that will describe it. Changes in the referenced object will be visible, but not changes OF THE OBJECT in its variable. – Kornelije Petak Sep 06 '11 at 21:13
  • Unless I'm misunderstanding, as long as it's a mutable reference type (class), you can do this. A variable of a reference type (class) is already a reference. Using ref on a reference type passes a reference to the reference. – James Michael Hare Sep 06 '11 at 21:14
  • If the saved reference and original reference both continue to reference the same object, they both see any changes made inside or outside that wrapper class. – James Michael Hare Sep 06 '11 at 21:16
  • 1
    The commenters are missing the point of the question. 1) T is not constrained to a reference type. 2) He wants to capture a reference to the *variable* (he cannot do this, for the record), so that when the *variable* changes, he'll see it. @Kornelije, if you can confirm that you want to observe changes to the variable, and not just the variable's original object, that might help. Or maybe I'm the one missing the point, you can confirm that, too. – Anthony Pegram Sep 06 '11 at 21:18
  • @Anthony: Thanks for the reply. It seems you did understand my intentions (and my bad English). I have posted some more code that would explain what I need. And I guess you have already answered the question :) – Kornelije Petak Sep 06 '11 at 21:20
  • @Kornelije: Okay, now we see, you want a reference to your reference so that when the reference itself changes its target, you want the stored reference to do the same. Wasn't sure if you were talkign about changes inside the object, or to the reference. But it is the reference. – James Michael Hare Sep 06 '11 at 21:23
  • @Kornelije: after seeing the update, Anthony is right, you can't store a reference to a variable. You could wrap it in a new class called ListManager and then have both your program and the class hold a reference to that, so that the reference is one level removed... – James Michael Hare Sep 06 '11 at 21:25
  • @James: Thanks. I just wanted to be sure that there is nothing intrinsic to C# that enables such a thing. Maybe something can be done with closure, but I guess I could slightly change the design. – Kornelije Petak Sep 06 '11 at 21:26
  • One thought is that you could capture it via a lambda, passing the lambda into the class instead of the variable directly. Lambdas close over the variable, so any changes to it would be observed elsewhere if you happened to pass it around. Not calling it a good idea, mind you, and I haven't fully thought it through, either. – Anthony Pegram Sep 06 '11 at 21:29

3 Answers3

4

Sounds like you're looking for TypedReference and the __makeref keyword.

Warning: they're poorly documented and not in the standardized part of C#.

There's a lot more information in this question.

Community
  • 1
  • 1
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
0

All classes in C# are reference objects so what you have coded should update the value of SavedElement. However, if T is a primitive type (e.g., int, string, etc.), this would not work since these are set by value. You would need to put a constraint on T to make sure it's a class.

Garrett Vlieger
  • 9,354
  • 4
  • 32
  • 44
  • one slight correction - string is a reference type, it just happens to be an immutable reference type. You can have two string variables pointing to the exact same instance of a string. – James Michael Hare Sep 06 '11 at 21:18
0

You cannot normally capture a reference to a variable and store it as a property. One hackish solution (not really suggesting it's a good idea, I'd explore other avenues first) is to capture it in a closure and pass the closure around. Closures capture variables, not values. As a result, changes to variables can be observed elsewhere. For example, given

class Foo
{
    public int Baz { get; set; }
}

class Bar
{
    private Func<Foo> fooGetter;

    public Bar(Func<Foo> fooGetter)
    {
        this.fooGetter = fooGetter;
    }

    public void Do()
    {
        Console.WriteLine(fooGetter().Baz);
    }
}

You can have

Foo foo = new Foo() { Baz = 1 };
Bar bar = new Bar(() => foo);
bar.Do();
foo = new Foo() { Baz = 2 };
bar.Do();

Changes to the variable foo are observed, since that is what the caller enclosed in the lambda. Had the caller simply said () => new Foo(), you would (of course) not observe any changes.

Anthony Pegram
  • 123,721
  • 27
  • 225
  • 246