2

Is there a way in C# to have a method retain a variable across different calls? For example:

private void foo()
{
    int j; //declare this so as it isn't destroyed with the stack frame.
    int i = calculateSomeValue() + j;
    j = i;
}

The normal way I would do this would be with a member variable like this:

private int j = 0;
private void foo()
{
    int i = calculateSomeValue() + j;
    j = i;
}

I don't like how the variable j can be accessed in all of the other methods of the class. Plus it just seems messy this way: defining a member variable when it will only be used in one method to keep track of the value of i when the method was last called.

Is this possible in a nice/clean way? Or should I just use the member variable way and forget about it?

Danny Herbert
  • 1,912
  • 1
  • 16
  • 26
  • 2
    I don't think there's a way to give j class scope without defining it as a class field (or property). Interested to see if anyone knows another way. – Shannon Holsinger Sep 15 '16 at 14:41
  • 5
    If you've got state which none of the other methods are interested in, you might want to *consider* whether that's worth breaking out into a separate type in its own right. But in general, no, you should absolutely have an instance variable. It's part of the state of the object. – Jon Skeet Sep 15 '16 at 14:41
  • 2
    Sounds like a mini class trying to get out. – Matthew Watson Sep 15 '16 at 14:44
  • Why not create a class with only that variable and function? If the variable needs to be "protected" that's probably the best practice. – r41n Sep 15 '16 at 14:45
  • 1
    Not possible in C#. You can only do this in VB.Net using the [static variable](https://msdn.microsoft.com/en-us/library/z2cty7t8.aspx) declaration. – Gabriel GM Sep 15 '16 at 14:54
  • Very nice, I think i'll leave it as a member variable for now and refactor it if it needs it. – Danny Herbert Sep 15 '16 at 14:55

4 Answers4

3

You could use a tiny little nested class to encapsulate it, along these lines:

public class Test
{
    private int foo()
    {
        return nested.foo();
    }

    private int calculateSomeValue()
    {
        return 42;
    }

    readonly Nested nested = new Nested();

    private class Nested
    {
        private int j;

        public int foo()
        {
            int i = calculateSomeValue() + j;
            j = i;
        }
    }
}

The methods in the outer class will only be able to access the public members of Nested, so they can only access foo() in this example - j is inaccessible. But note that methods in Nested have access to all the private members of the outer class.

Matthew Watson
  • 96,889
  • 9
  • 144
  • 240
  • Why is this better, than using a simple instance variable? Should we introduce a new class whenever we want to keep some value in a field ? – Fabjan Sep 15 '16 at 14:54
  • @Fabjan The OP said it was important that the variable not be accessible to other methods of the class. – Servy Sep 15 '16 at 14:56
  • @Fabjan I wouldn't usually bother for such simple code as this, but it scales well to slightly more complicated code where you want to encapsulate some behaviour, but it's not so complex that you should introduce a whole new non-nested class. However, in reality classes should normally be small enough that this kind of thing never becomes attractive. – Matthew Watson Sep 15 '16 at 15:00
  • @MatthewWatson Well, this is a nice piece of code indeed but it seem to me like a little overkill for the task it handles. – Fabjan Sep 15 '16 at 15:07
1

I don't think there is another way of giving the scope you ask. The fact that other methods can access j in this case being a member variable is a direct consequence of the OOP concepts your are using encapsulating the members inside the holder object.

So I would continue using the member variable and don't worry about other methods being able to access it. If for some reason you must avoid other methods accessing the variable, maybe you should consider refactoring in its own type although maybe with the example given is not justified.

Hope this helps.

Karel Tamayo
  • 3,565
  • 2
  • 23
  • 30
0

In C this is possible with static local variables

void foo()
{
     static int j;
     int i = calculateSomeValue() + j;
     j = i;
}

In C# this functionality was intentionally not included.

You have a couple of options, which depends on the intended lifetime of j. If you just need it for successive calls and for some reason don't want to use a loop, you can go for a recursive approach

private void foo()
{
    foo(0);
}

private void foo(int j)
{
    int i = calculateSomeValue() + j;
    foo(j);
}

If you want more control or a longer lifetime, the objected oriented way would be to push this functionality onto a class, which can be a normal class or private and nested in your existing class.

public class ParentClass()
{
    FooClass bar = new FooClass();

    private class FooClass()
    {
        private int j = 0;
        public void foo()
        {
            int i = calculateSomeValue() + j;
            j = i;
        }

        private int calculateSomeValue()
        {
            //
        }
    }

    private void DoStuff()
    {
        bar.foo();
    }
}

Also I suggest re-weighing if its worth it to protect the variable in the first place.

plast1k
  • 763
  • 8
  • 15
  • That's not the same though: A static local in C is shared with ALL instances of that class. The field in C# is separate for each instance. – Matthew Watson Sep 15 '16 at 15:01
  • C doesn't have classes (though static would basically be global in scope), perhaps C++ you would need to be more careful but I'm not experienced there. Regardless both C# methods I posted would be contained within the scope of the class. – plast1k Sep 15 '16 at 15:04
0

There is a nasty way to do that using closures. But that means you have to define your methods as anonymous functions and have them wrapped in some global method where they are defined. Consider this more an academical exercise and definitely not a production solution. For a real life solution definitely consider a separate class.

static void Main(string[] args)
{
    var j = 0;

    Func<int> calculateSomeValue = () =>
    {
        return 41;
    };

    Action myFoo = () =>
    {
        int i = calculateSomeValue() + j;
        j = i;
    };
}
PiotrWolkowski
  • 7,942
  • 6
  • 43
  • 65