-2

I've got int variable lets say:

int x = 0;

then I pass it to the class which should hold it and which has Print method which prints current value of that variable.

Then in another class I change the i value and as a result I would like to have in MyPrinter class after calling print new value of the i variable printed. Is it possible?

sample code below:

int x = 0;
MyPrinter printer = new MyPrinter(x);
printer.Print(); //Expected result is 0
x++;
MyPrinter.Print(); //Expected result is 1
Kumar V
  • 8,810
  • 9
  • 39
  • 58
Snorlax
  • 787
  • 2
  • 9
  • 22
  • 3
    This is not possible. – SLaks Mar 07 '14 at 21:23
  • Can you change your `MyPrinter()` constructor, or is only int? – Erik Philips Mar 07 '14 at 21:24
  • why ? Why designers of the language didnt allow such structure ? – Snorlax Mar 07 '14 at 21:25
  • 5
    Because it's unnecessary and ugly? – Ant P Mar 07 '14 at 21:25
  • There are a number of ways to do this, make X a public property (preferably with a more descriptive name), pass it by ref to a whole bunch of places (but that would be hard) are just a few. – BradleyDotNET Mar 07 '14 at 21:25
  • I would prefer to leave int but if necessary it can be different type but then it is easier and I know how to do this:/ – Snorlax Mar 07 '14 at 21:25
  • And extremely ambiguous. – Erik Philips Mar 07 '14 at 21:25
  • 2
    Can't be done, and I'm glad about it – Tony Hopkinson Mar 07 '14 at 21:28
  • 1
    Why, because it's a guaranteed bugfest. – Tony Hopkinson Mar 07 '14 at 21:29
  • The int is a value type. A value type holds the data in its memory allocation. You are looking for reference type behavior; a reference type is a pointer to a memory allocation. You can create a reference type wrapper around the in by encapsulating it in your own class. You could then pass the reference type into your printer class. See [this](http://msdn.microsoft.com/en-us/library/t63sy5hs.aspx) MSDN page for information on reference and value types. – codechurn Mar 07 '14 at 21:30
  • What is wrong about it ? Please give me an example why I dont have easy way of storing reference to an int and I can store reference to obejct types ? what is the difference from business point of view ? – Snorlax Mar 07 '14 at 21:31
  • Right your code x goes up as expected, then all of a sudden becomes - 6. Where's the bug? Business case is robust, maintainable, testable code. – Tony Hopkinson Mar 07 '14 at 21:35
  • he ? Why all of a sudden ? If its wrapped with a class then I remember about each change ? phi – Snorlax Mar 07 '14 at 21:37
  • @user3388684 But if you do this with anything other than trivial use cases, you're going to end up with many classes holding a reference to this variable and it'll be impossible to perform proper state management without having all kinds of dependencies between classes. A better question is why would you ever need to do this? – Ant P Mar 07 '14 at 21:39
  • So in order to have a reference to a value, which if you could do it, could be in any instance of any class, you are going to write a load more code to tell you how it went wrong? Again. If you want to write code like this use C or C++ not C# – Tony Hopkinson Mar 07 '14 at 21:42
  • I just want to say that it is in my opinion stupid that I have to create wrapper which then I pass to each cllass in which I want to have access to current int variable value. Thats all.. If I have to than I ll do it but Your arguments are sensless because I can change that wrapper class instance the same way I can change simple int value from any place in my code so its still bugfest yeah ? – Snorlax Mar 07 '14 at 22:02
  • The main difference is that the wrapper class is able to execute logic as the value changes. Furthermore, Lee's answer does allow you to store a reference as a delegate, which is more like your goal. Generally, good design is to avoid letting any place in your code modify a value - to restrict it absolutely as much as possible. C#, however, allows you as much freedom as you want, to the point of directly using pointers (in an `unsafe` block). Ultimately, though, its a matter of careful design. – Magus Mar 07 '14 at 22:17
  • 1
    The designers of C# decided to disallow storing references to *variables* because (1) that is one of the largest class of crashing bugs in C and C++, and (2) there is almost always a better way to achieve your goal without storing a reference to a variable. You are free to believe that this is quite stupid of course, but I would suggest to you that you spend your valuable time studying how to make a more well-factored solution. If you want to have an operation produce a side effect then the standard way to do that is to use the observer pattern: make an event. – Eric Lippert Mar 07 '14 at 23:36
  • 3
    @user3388684 (if that is your real name) I think you've been fairly disrespectful to a lot of the commenters/answerers here, despite them posting to help you out, with some fairly unique suggestions. I think you should reconsider your attitude on a site where people are donating their time for no compensation. – Jon Barker Mar 08 '14 at 03:15

5 Answers5

7

When an int is passed to a method or constructor it is passed by value and the callee gets a copy of the value. This copy is independent of the original and hence updates to one are not reflected on the other. It is possible to pass it by ref but that isn't suitable here because ref can't be used on a field of a type.

In order to make this work you are going to need to put the int value into a class and then pass that class into MyPrinter.

class Container {
  public int Value;
}

Container x = new Container();
MyPrinter printer = new MyPrinter(x);
printer.Print();
x.Value++;
MyPrint.Print();  // x.Value is 1 
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • oh great, so I have to build wrapper class just to store reference to simple int variable. Really clean solution... @Ant P – Snorlax Mar 07 '14 at 21:34
  • @user3388684 if you want to store and share a reference to an `int`, or any other value type, with objects in the heap then yes this is the only way to do it. – JaredPar Mar 07 '14 at 21:38
  • I had a very nice answer written up which is basically the same as this one (with more explanation as to what is going on in various cases...) but submitting didn't submit. – EtherDragon Mar 07 '14 at 21:46
  • Also, could one just use `Int` instead of making a wrapper class? – EtherDragon Mar 07 '14 at 21:47
  • Yeah SO, hiccupped on me too. Probably us lot hammering this question. :) – Tony Hopkinson Mar 07 '14 at 21:48
  • 2
    @EtherDragon No - `Int32` and `int` are the same thing. – Ant P Mar 07 '14 at 21:53
5

You could change your class to take a Func<int> and close over x instead e.g.

public class Printer
{
    private readonly Func<int> f;
    public Printer(Func<int> f)
    {
        this.f = f;
    }

    public void Print()
    {
        Console.WriteLine(f());
    }
}


MyPrinter printer = new MyPrinter(() => x);
printer.Print(); //Expected result is 0
x++;
MyPrinter.Print(); //Expected result is 1
Lee
  • 142,018
  • 20
  • 234
  • 287
  • Depending on compiler optimizations, this may not work as expected. – Haney Mar 07 '14 at 21:28
  • 1
    @DavidHaney - Which compiler optimisations are those? – Lee Mar 07 '14 at 21:33
  • 1
    In the case of closure over a loop, this could have unexpected side effects that are good to be aware of. It seems this code is probably heading towards a looped print-out, but that's just my guess. – Haney Mar 07 '14 at 21:35
  • @DavidHaney What side effects? – Ant P Mar 07 '14 at 21:35
  • This answer causes the least change to the code in the original question, which I definitely approve of. It makes a pure observer class, which is a nice idea in this case. – Magus Mar 07 '14 at 21:39
  • why wouldn't it work in 2012? As far as I know the only change was http://stackoverflow.com/questions/12112881 which doesn't apply here. – John Gibb Mar 07 '14 at 21:51
  • 1
    @JonBarker - There is no change in closure logic in C# 5. The change you are referring to is that loop variables are always local to a foreach rather than being declared outside. – Lee Mar 07 '14 at 21:51
  • Apologies - i just validated it and this works correctly in 2012. – Jon Barker Mar 07 '14 at 21:53
3

As others have pointed out, you can't do this without either wrapping the int up in a reference type or doing some other funky stuff.

However, I think this misses the point, which is that you're addressing the problem all wrong. Here's a more OOP-friendly way of approaching your problem:

public class Printer
{
    public int Value { get; set; }

    public Printer(int x)
    {
        Value = x;
    }

    public void Print()
    {
        Console.WriteLine(Value);
    }
}

public class Program
{
    public static void Main()
    {
        var printer = new Printer(0);

        printer.Print();    
        printer.Value++;        
        printer.Print();
    }
}
Ant P
  • 24,820
  • 5
  • 68
  • 105
  • No no no, I want the obejct of a class lets say observe particular int variable and always print its exact value which can be changed from lot of different places – Snorlax Mar 07 '14 at 21:39
  • 1
    The correct way to do that in OOP is to make the int a property of the `Printer` and then pass the `Printer` itself to the other classes that need to manipulate it. Basically, you're doing it wrong. – Ant P Mar 07 '14 at 21:41
  • @user3388684: A variable that can be changed from many different places is innately bad design, because you can't always be sure where it was last changed. Still, this is about the same. – Magus Mar 07 '14 at 21:41
  • @Ant P -> lol. I can have reference to Printer in hundreds of places and then it is ok right ? pff just stop writing sensless comments – Snorlax Mar 07 '14 at 21:57
  • @user3388684 (if that is your real name) I think you've been fairly disrespectful to a lot of the commenters/answerers here, despite them posting to help you out, with some fairly unique suggestions. I think you should reconsider your attitude on a site where people are donating their time for no compensation – Jon Barker Mar 08 '14 at 03:16
1

This cannot be done, unless you wrap the int in a reference type so that MyPrinter holds the int as part of a reference to the same object you're modifying.

EDIT: @JaredPar has the same answer, but with a clean code example! :)

Haney
  • 32,775
  • 8
  • 59
  • 68
1

In order for this to work, you need to pass something that is a reference type instead of a value type. Instance primitives are always passed by value, but there is nothing preventing you from passing the instance - but this would require a redesign of your classes: Consider the following (c#-like pseudocode)...

public class Container
{
    public int x;
}

... // some implementation in another class
Container myContainer = new Container();
myContainer.x = 0;

MyPrinter printer = new MyPrinter(myContainer);
printer.Print(); //Result is 0
myContainer.x ++;
MyPrinter.Print(); //Result is 1

This assumes that the MyPrinter class knows what to do with a Container instance.

What's going on here? Well a primitive, like int is passed by value - which means that the program makes a copy when passing it into the method:

int x = 0;
MyPrinter printer = new MyPrinter(x); // Makes a copy of x, and passes the copy to MyPrinter
printer.Print(); //Expected result is 0 - correct
x++; // Increments the local variable, but not the copy
MyPrinter.Print(); //Expected result is 1 - incorrect because only the local variable is updated.

As opposed to:

Container myContainer = new Container();
myContainer.x = 0;

MyPrinter printer = new MyPrinter(myContainer); // Passes the reference to the same instance
printer.Print(); //Result is 0 - because it uses the variable attached to the instance
myContainer.x ++; // Increments the variable attached to the instance
MyPrinter.Print(); //Result is 1 - because the instance is still the same in both places - due to being passed by reference.
EtherDragon
  • 2,679
  • 1
  • 18
  • 24
  • Oh! It did submit... Basically an expansion of JaredPar's answer above. And yes, having a public variable is a terrible idea - but that is not particularly relevant to the question, at this time. – EtherDragon Mar 07 '14 at 21:49
  • 1
    I know this is overly pedantic, but reference types are also passed by value in C#, where passed value has all the same references. The difference can be meaningful, because even an integer reference type could not be modified in one place and observed in another, because it's reassignment would change the reference. A reference type *wrapping* an integer exhibits what you and many others call reference type behavior. This is why the `ref` and `out` keywords exist. – Magus Mar 07 '14 at 21:51