2

My problem, narrowed down to a simple explaination, is the following: I have a class which needs to work with a number (without changing it) which is subject to change. This number doesn't necessarily come from another class, and it can be anything. But I'd like to only "give" it to the class once, instead of constantly having to call update methods or having to create a wrapper (since again, as I said, this should work with any kind of number and having to wrap up everything is kind of unpratical).

Here's some code, hoping it helps:

public class SimpleExample
{
    int value;
    public SimpleExample(int variableOfWhichINeedAReference)
    {
        //Of course this won't work, but I'll keep it simple.
        value = variableOfWhichINeedAReference; 
    }
    public void DisplayValue()
    {
        print(value);
    }
}
public class RandomClass
{
    int myValue = 10;
    SimpleExample s = new SimpleExample(myValue);

    public void WorkWithValue()
    {
        myValue++;
    }

    public void Display()
    {
        print(foo);
        print(bar);
        s.DisplayValue();
    }

}

Now, the problem seems pretty obvious: If I instantiate a SimpleExample and give it a variable as a parameter, it will get its value rather than a reference to it. Is there a simple enough way that can avoid me the creation of a wrapper? Thanks.

KappaG3
  • 660
  • 5
  • 14
  • 1
    Use the `ref` modifier in the SimpleExample constructor parameter list? – Adrian Godong Apr 19 '13 at 17:25
  • in short, just create a wrapper. http://stackoverflow.com/questions/2980463/how-do-i-assign-by-reference-to-a-class-field-in-c/2982037#2982037 – Filip Apr 19 '13 at 17:25

3 Answers3

7

Make a really simple class:

class Ref<T>
{
    public T Value;
    public Ref<T>()
    {
    }
    public Ref<T>(T value)
    {
        this.Value = value;
    }
}

Then use it like this:

class A
{
    Ref<int> x;
    public A(Ref<int> x)
    {
        this.x = x;
    }
    public void Increment()
    {
        x.Value++;
    }
}

...

Ref<int> x = new Ref<int>(7);
A a = new A(x);
a.Increment();
Debug.Assert(x.Value == 8);

Note that the Ref<T> class here is a reference to a value - not a reference to a variable. If you want a reference to a variable, use Eric Lippert's solution (as pointed out by Filip).

Community
  • 1
  • 1
Timothy Shields
  • 75,459
  • 18
  • 120
  • 173
  • 1
    Note that this is creating a wrapper class. The OP *specifically* said he didn't want to do that. – Servy Apr 19 '13 at 17:32
  • 2
    Also note that there's no real need for the `where T : struct`. This type works perfectly well with classes as well as structs, and it's *useful* for classes as well as structs, so why bother restricting it. – Servy Apr 19 '13 at 17:34
  • 2
    @Servy I understand that, however, I think the OP may find this is the best solution to the problem he has. So while it's not *exactly* what was asked for, I think it's very useful. It basically provides strongly-typed boxing (as opposed to `object`, which does not). – Timothy Shields Apr 19 '13 at 17:35
  • @TimothyShields I agree it's likely to be a good solution for the OP's real problem, but the fact remains that this is not an answer to the question as the question *specifically* prohibits this answer. – Servy Apr 19 '13 at 17:35
  • 4
    @Servy I'm betting on the OP being more concerned with solving his problem than dealing with SO legalism... – Timothy Shields Apr 19 '13 at 17:36
  • @Servy Good point about the `where T : struct` being without merit. I've removed that. – Timothy Shields Apr 19 '13 at 17:37
4

So what you want is not an int, but rather a way of getting an int at some point in time. There are several ways of doing this, one of which is to have your object accept a Func<int>. Then the code can pass in a method that returns the current value of...whatever, rather than the value at the time SimpleExample is created. Using a lambda to close over a variable makes doing this much easier as well.

public class SimpleExample
{
    Func<int> func;
    public SimpleExample(Func<int> func)
    {
        this.func = func;
    }
    public void DisplayValue()
    {
        print(func());
    }
}
public class RandomClass
{
    int myValue = 10;
    SimpleExample s;

    public RandomClass()
    {
        s = new SimpleExample(() => myValue);
    }

    public void WorkWithValue()
    {
        myValue++;
    }

    public void Display()
    {
        print(foo);
        print(bar);
        s.DisplayValue();
    }
}
Servy
  • 202,030
  • 26
  • 332
  • 449
  • good question, mine def. doesn't work - just tried it and you were right :/ – Steve's a D Apr 19 '13 at 18:03
  • If, instead of getting the int later you want to modify it, you can use Actions instead of Funcs, with 0 or more parameters and no return type/value. Funcs also handle parameters (put their types before the return type) if you want to do both. – Moige May 05 '21 at 11:13
1

There is no standard wrapper for the purpose you seek, though a single-element array could be used for that purpose. Alternatively, one could define a simple wrapper type:

public class ExposedValueHolder<T> { public T Value; } // Really simple class, eh?

and then use an ExposedValueHolder<YourStructType> to wrap your object. It's not possible in general to capture something passed as an arbitrary ref parameter, since objects may live indefinitely but byrefs (the things which are actually passed when using ref parameters) may die any time after function they're passed to goes out of scope.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • @Filip: The ExposedValueHolder is like what the answer suggests, though I thought it worth noting that an array would be another option. – supercat Apr 19 '13 at 18:09