2

I have an existing method like below: the method body has over 1000 lines of code that I can't bring it here, just in method body if the value of argument (I mean index_param) be changed then surly I get wrong results.

public String calculateValue(int index_param) {
    //a highly complicated transactional method which returns a String
}

I want to be sure the method body is not changing index_param during operation, and prevent it from changing the parameter. what do you prefer for this scenario?

void
  • 7,760
  • 3
  • 25
  • 43
  • 5
    If it is a parameter it can be used, consumed, changed, returned, etc. Who do you actually want to prevent from changing the parameter? If it is internal to the method, wouldn't you be the one changing the value? – David L Dec 29 '14 at 03:51
  • 1
    I'm not sure this is a question based in knowing how C# treats that argument. If the body of that method did change the index_param value, the calling code would not be effected (if that's what you're worried about). – ventaur Dec 29 '14 at 03:54
  • 5
    Then your problem is that the method has 4500 lines of code. A method should NEVER be that long. Break it up into smaller components. – mason Dec 29 '14 at 03:57
  • Brothers I just want to make the **index_param** unchangeable, how can I do it(best way)? – void Dec 29 '14 at 03:57
  • You can't. C# will always allow someone somewhere to come in behind you and change it if it is an argument – David L Dec 29 '14 at 03:58
  • You cannot make a parameter read-only in C#. If you have [ReSharper](http://www.jetbrains.com/resharper/), you could search for usages of that parameter and verify that none are writes. – Michael Liu Dec 29 '14 at 03:58
  • @MichaelLiu ReSharper isn't necessary. In Visual Studio, just right click and do "Find All References" and examine the lines to make sure they aren't edits. Or in any text editor, use the "find" feature (usually Control F) to find all instances of `index_param` to see if they're editing the value. – mason Dec 29 '14 at 03:59
  • 2
    @mason: ReSharper allows you to filter usages by reads/writes. Manually examining hundreds of matching lines might be error prone. – Michael Liu Dec 29 '14 at 04:07
  • @MichaelLiu Yes, I'm aware of ReSharper's capabilities, and also the price tag. The root issue here is that he has a method with 4500 lines of code. Let's focus on the important issue! – mason Dec 29 '14 at 04:08
  • Related post - [Const function parameter in C#](https://stackoverflow.com/q/10981888/465053) – RBT Jan 13 '22 at 06:20

5 Answers5

3

Unfortunately, C# doesn't have a local variable analogue of readonly.

You could use a Code Contract Assert Requires pseudo post-condition for this, although I can't see a way to avoid an additional local variable which could also, in theory, be mutated.

public String calculateValue(int index_param) {
    int __DO_NOT_MUTATE_ME = index_param;

    // a highly complicated transactional method which returns a String
    //

    Contract.Assert(index_param == __DO_NOT_MUTATE_ME, "No! Don't change the local var");
    return result;
}

Edit - Some findings
Code Contracts aren't as suited toward detecting stack variable mutation as expected.

  • Contract.Ensures() must be at the top of the code block, and Ensures is only designed to reason over return values, not local vars.
  • Contract.Assert is thus cleaner as it can be placed anywhere in the block. It throws if the stack variable is mutated at run time.
  • If static analysis is enabled, there is SOME (post) compile time benefit as well, as per the attached images (Top shows the warnings with the bad code, bottom shows no warnings with no mutation). However, instead of flagging the bad line of code which mutates the index_param (marked with Red Arrow), it instead flags a warning with our contract as the nonsensical "Consider adding Contract.Requires(index_param + 1 == index_param);". Hmmm ...
  • You can download the Code Contracts plugin for Visual Studio here - an additional tab is added to the Properties dialog.

enter image description here

StuartLC
  • 104,537
  • 17
  • 209
  • 285
  • Interesting suggestion. This would make sense if you used the local boolean value to immediately throw an exception. Without an exception, this doesn't solve for the original issue, as you note. – David L Dec 29 '14 at 04:00
  • 1
    and this will still be a runtime check not compile time. so you wont "fail early" – gp. Dec 29 '14 at 04:03
  • @mason hence the rather ungainly name. I'm guessing the OP's real problem is more related to unsafe PInvoke or ComInterop code which may be inadvertently futzing the stack. The chances of mutating two local vars in exactly the same way are slim, IMO. – StuartLC Dec 29 '14 at 04:03
  • @gp We get some design time checking too (the Contract Checker static analysis runs in the background shortly after compile). But as noted above, the static checker flags the Contract instead of our smelly code :( – StuartLC Dec 29 '14 at 05:01
  • I guess the ultimate solution would be for the C# language to support `readonly` stack variables (params and locals) like java `final` does, and potentially also the `const` correctness syntax of C++ (which is more like [Pure](http://msdn.microsoft.com/en-us/library/system.diagnostics.contracts.pureattribute(v=vs.110).aspx)). So hopefully Code Contracts will be a sign of things to come in future! – StuartLC Dec 30 '14 at 17:53
3

The parameter is passed by value. So, original reference doesn't change anyways even if you reassign the variable to a new value in the method body.

In case, you don't want method body to even reassign the variable to a new value, something like final int index_param in java or const int index_param in c++, there is no equivalent in c#.

gp.
  • 8,074
  • 3
  • 38
  • 39
2

If you're really paranoid, you can wrap your value in a helper class that ensures your value does not change

public static class ReadOnly
{
    public static ReadOnly<T> Create<T>(T val) where T : struct
    {
        return new ReadOnly<T>(val);
    }
}

public class ReadOnly<T> where T : struct
{
    private readonly T _value;

    public ReadOnly(T val)
    {
        this._value = val;
    }

    public T Value { get { return this._value; } }
}

public String calculateValue(int index_param) {
    //a highly complicated transactional method which returns a String
    var read_only_index_param = ReadOnly.Create(index_param);
    Console.WriteLine(read_only_index_param.Value);
}
Steven Wexler
  • 16,589
  • 8
  • 53
  • 80
1

Based on the comments, if it is an argument, it can be modified.

The only way to prevent the original value from being modified is to create a constant value INSIDE of the method rather than allow it to be injected into the method as an argument.

public String calculateValue() {
    const int index_param = 2;
}

A second option would be to add a readonly private field and move your index_param argument passing to the enclosing class' constructor. This way the field could not be changed by your calculateValue() method, although this would require a refactor.

public class ContainingClass
{
    private readonly int _index_param;

    public ContainingClass(int index_param)
    {
        _index_param = index_param;
    }
}

This would allow you to use your private field inside of calculateValue and it could not be changed, which would be a compile-time check.

Please note that this private field could still be changed via reflection. That said, if you have anyone that desperate to sabotage your codebase, you have larger issues on your hands.

David L
  • 32,885
  • 8
  • 62
  • 93
0

Code contracts and/or unit tests are the best solutions but an alternative would be to simply assign the parameter to a local variable and verify that the parameter hasn't changed before returning a value from the method. If it does, you'll have to throw an exception since your code is potentially wrong.