95

I'm looking for the C# equivalent of Java's final. Does it exist?

Does C# have anything like the following:

public Foo(final int bar);

In the above example, bar is a read only variable and cannot be changed by Foo(). Is there any way to do this in C#?

For instance, maybe I have a long method that will be working with x, y, and z coordinates of some object (ints). I want to be absolutely certain that the function doesn't alter these values in any way, thereby corrupting the data. Thus, I would like to declare them readonly.

public Foo(int x, int y, int z) {
     // do stuff
     x++; // oops. This corrupts the data. Can this be caught at compile time?
     // do more stuff, assuming x is still the original value.
}
John Saunders
  • 160,644
  • 26
  • 247
  • 397
Nick Heiner
  • 119,074
  • 188
  • 476
  • 699
  • 12
    I dont think its exact duplicate. That question is about the difference between pass by reference and pass by value. I think Rosarch may have used bad example code for the point he was trying to get across. – Corey Sunwold Feb 26 '10 at 02:32
  • @Rosarch: Of what I'm understanding from the word "final", is that you can do nomore action with the object, whatever what that might be. I understand that "final" applied to a class would be the equivalence of "sealed" in C#. But what is the benefit or real usage of this keyword "final" anyway? I have seen it almost in any place someday in a JAVA source code. – Will Marcouiller Feb 26 '10 at 03:10
  • 3
    @Will Marcouiller With the final keyword its not the object that can't change, because you can use method that change in internals of the object, its the reference to the object that can't change. In the case of a pass by value situation, like the example that was given, it would prevent x++ from being a valid operation because the value would be changing. The advantage would be that you get a compile time sanity check to make sure nothing sets the value to be different. However, in my experience I have never needed such a feature. – Corey Sunwold Feb 26 '10 at 03:16
  • +1 @Corey Sunwold: Thanks for this precision. As for now, I only know similitudes between C# and JAVA, knowing that C# "inherited" from JAVA in its concepts. This said, it encourages me to learn more about JAVA as I know it is well spread worldwide. – Will Marcouiller Feb 26 '10 at 15:43

10 Answers10

65

Unfortunately you cannot do this in C#.

The const keyword can only be used for local variables and fields.

The readonly keyword can only be used on fields.

NOTE: The Java language also supports having final parameters to a method. This functionality is non-existent in C#.

from http://www.25hoursaday.com/CsharpVsJava.html

EDIT (2019/08/13): I'm throwing this in for visibility since this is accepted and highest on the list. It's now kind of possible with in parameters. See the answer below this one for details.

Tim Coker
  • 6,484
  • 2
  • 31
  • 62
Corey Sunwold
  • 10,194
  • 6
  • 51
  • 55
  • 1
    "Unfortunately"? How would that differ from the current capability? – John Saunders Feb 26 '10 at 02:34
  • 35
    @John Saunders Unfortunate because Rosarch is looking for a feature that doesnt exist. – Corey Sunwold Feb 26 '10 at 02:43
  • @Corey: he's looking for a feature with no effect. In his example, the parameter would be constant without the `const`. – John Saunders Feb 26 '10 at 03:05
  • 8
    @John: It's not constant within the method. That's the issue. – Noon Silk Feb 26 '10 at 03:07
  • 7
    Its only constant outside the scope of this method. Ignoring whether it was passed by reference or by value, Rosarch doesn't want the parameter to change within the scope of the method. Thats the key difference. – Corey Sunwold Feb 26 '10 at 03:13
  • 6
    @silky: reading his post, that's not what he's asking for. He's asking to ensure the method doesn't change the actual parameters. – John Saunders Feb 26 '10 at 05:14
  • @JohnSaunders it could greatly differ from the current capability in the way of performance. In ~80% of my functions I'd rather just give a value by a reference, and be sure that this won't be changed, than allow creating absolutely unneeded copy of an object. Btw, in the way of performance for me, as a man from the C/C++ world, C# is a flaming hell. – Hi-Angel Oct 16 '14 at 16:38
  • 2
    @Hi-Angel: learn more about C# before judging. Only value type objects are copied: `int`, `double`, `struct`. Note that `struct` in C# is different from `struct` in C++. – John Saunders Oct 16 '14 at 16:55
48

This is now possible in C# version 7.2:

You can use the in keyword in the method signature. MSDN documentation.

The in keyword should be added before specifying a method's argument.

Example, a valid method in C# 7.2:

public long Add(in long x, in long y)
{
    return x + y;
}

While the following is not allowed:

public long Add(in long x, in long y)
{
    x = 10; // It is not allowed to modify an in-argument.
    return x + y;
}

Following error will be shown when trying to modify either x or y since they are marked with in:

Cannot assign to variable 'in long' because it is a readonly variable

Marking an argument with in means:

This method does not modify the value of the argument used as this parameter.

Max
  • 12,622
  • 16
  • 73
  • 101
  • 7
    Of course, it's not useful for reference types. Using `in` keyword for parameters with those types, only prevent assigning to them. Not prevent modifying its accessible fields or properties! Am I right? – ABS Aug 01 '19 at 06:47
  • 9
    **Please note** that it only works well with struct/values and NOT with classes, where you can modify the instance which is by ref so that's much worse. And you get no warnings whatsoever. Use with caution. http://blog.dunnhq.com/index.php/2017/12/15/readonly-parameters-in-c-a-step-closer-to-immutability/ – jeromej Feb 12 '20 at 09:09
  • 1
    This won't work with async methods: "CS1988 Async methods cannot have ref, in or out parameters." – UserControl Mar 22 '23 at 19:06
10

The answer: C# doesn't have the const functionality like C++.

I agree with Bennett Dill.

The const keyword is very useful. In the example, you used an int and people don't get your point. But, why if you parameter is an user huge and complex object that can't be changed inside that function? That's the use of const keyword: parameter can't change inside that method because [whatever reason here] that doesn't matters for that method. Const keyword is very powerful and I really miss it in C#.

8

Here's a short and sweet answer that will probably get a lot of down votes. I haven't read all of the posts and comments, so please forgive me if this has been previously suggested.

Why not take your parameters and pass them into an object that exposes them as immutable and then use that object in your method?

I realize this is probably a very obvious work around that has already been considered and the OP is trying to avoid doing this by asking this question, but I felt it should be here none-the-less...

Good luck :-)

Bennett Dill
  • 2,875
  • 4
  • 41
  • 39
  • 1
    Because members can still be modified. This c++ code shows that: `int* p; *p = 0;`. That will compile and run until the segmentation fault. – Cole Tobin Oct 27 '12 at 01:19
  • 1
    I down voted because it's not a solution to the problem. You could also save the arguments at the function head and compare them at the end, and throw an exception on change. I'm going to keep that in my back pocket if there is ever a worst-solution contest:) – Rick O'Shea Apr 20 '18 at 16:16
7

Create an interface for your class that has only readonly property accessors. Then have your parameter be of that interface rather than the class itself. Example:

public interface IExample
{
    int ReadonlyValue { get; }
}

public class Example : IExample
{
    public int Value { get; set; }
    public int ReadonlyValue { get { return this.Value; } }
}


public void Foo(IExample example)
{
    // Now only has access to the get accessors for the properties
}

For structs, create a generic const wrapper.

public struct Const<T>
{
    public T Value { get; private set; }

    public Const(T value)
    {
        this.Value = value;
    }
}

public Foo(Const<float> X, Const<float> Y, Const<float> Z)
{
// Can only read these values
}

Its worth noting though, that its strange that you want to do what you're asking to do regarding structs, as the writer of the method you should expect to know whats going on in that method. It won't affect the values passed in to modify them within the method, so your only concern is making sure you behave yourself in the method you're writing. There comes a point where vigilance and clean code are the key, over enforcing const and other such rules.

Steve Lillis
  • 96
  • 1
  • 3
  • That is actually pretty clever. Also I do want to note you can inherit multiple interfaces at the same time - but you can only inherit one class. – Natalie Adams Sep 08 '13 at 03:20
  • This is helpful... and does in same way relieve the annoyance of it missing from c#. Thanks – Jimmyt1988 Mar 05 '15 at 16:46
  • This type of coding should never reach Release builds, as you would have added two unnecessary conversion to and back from struct and yielding performance issue if run on data streams. – SoLaR Jun 07 '21 at 05:48
7

I'll start with the int portion. int is a value type, and in .Net that means you really are dealing with a copy. It's a really weird design constraint to tell a method "You can have a copy of this value. It's your copy, not mine; I'll never see it again. But you can't change the copy." It's implicit in the method call that copying this value is okay, otherwise we couldn't have safely called the method. If the method needs the original, leave it to the implementer to make a copy to save it. Either give the method the value or do not give the method the value. Don't go all wishy-washy in between.

Let's move on to reference types. Now it gets a little confusing. Do you mean a constant reference, where the reference itself cannot be changed, or a completely locked, unchangeable object? If the former, references in .Net by default are passed by value. That is, you get a copy of the reference. So we have essentially the same situation as for value types. If the implementor will need the original reference they can keep it themselves.

That just leaves us with constant (locked/immutable) object. This might seem okay from a runtime perspective, but how is the compiler to enforce it? Since properties and methods can all have side effects, you'd essentially be limited to read-only field access. Such an object isn't likely to be very interesting.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • 2
    I downvoted you for misunderstanding the question; it's not about changing the value AFTER the call, but changing it WITHIN it. – Noon Silk Feb 26 '10 at 03:05
  • 2
    @silky - I didn't misunderstand the question. I'm talking about within the function as well. I'm saying it's a weird restriction to send to a function, because it doesn't really stop anything. If someone forgets the original parameter changed they're just as likely to forget a copy changed. – Joel Coehoorn Feb 26 '10 at 03:15
  • 1
    I disagree. Merely providing a comment explaining the downvote. – Noon Silk Feb 26 '10 at 03:17
  • DateTime has read-only field access only, but it's still interesting. It has many methods which return new instances. Many functional languages avoid methods with side-effects and prefer immutable types, in my opinion this makes code easier to follow. – Devin Garner Jul 03 '13 at 20:28
  • DateTime is also still a value type, not a reference type. – Joel Coehoorn Feb 23 '15 at 18:01
  • this answer is wrong in the part it claims that value types are copied to be used by a function that takes it by `in` Defining methods using in parameters is a potential performance optimization. Some struct types ... the cost of copying those structures is critical. Passing those arguments by reference avoids the (potentially) expensive copy. ^ straight from https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/in-parameter-modifier – ComradeJoecool Apr 08 '20 at 15:29
3

I know this might be little late. But for people that are still searching other ways for this, there might be another way around this limitation of C# standard. We could write wrapper class ReadOnly<T> where T : struct. With implicit conversion to base type T. But only explicit conversion to wrapper<T> class. Which will enforce compiler errors if developer tries implicit set to value of ReadOnly<T> type. As I will demonstrate two possible uses below.

USAGE 1 required caller definition to change. This usage will have only use in testing for correctness of your "TestCalled" functions code. While on release level/builds you shouldn't use it. Since in large scale mathematical operations might overkill in conversions, and make your code slow. I wouldn't use it, but for demonstration purpose only I have posted it.

USAGE 2 which I would suggest, has Debug vs Release use demonstrated in TestCalled2 function. Also there would be no conversion in TestCaller function when using this approach, but it requires a little more of coding of TestCaller2 definitions using compiler conditioning. You can notice compiler errors in debug configuration, while on release configuration all code in TestCalled2 function will compile successfully.

using System;
using System.Collections.Generic;

public class ReadOnly<VT>
  where VT : struct
{
  private VT value;
  public ReadOnly(VT value)
  {
    this.value = value;
  }
  public static implicit operator VT(ReadOnly<VT> rvalue)
  {
    return rvalue.value;
  }
  public static explicit operator ReadOnly<VT>(VT rvalue)
  {
    return new ReadOnly<VT>(rvalue);
  }
}

public static class TestFunctionArguments
{
  static void TestCall()
  {
    long a = 0;

    // CALL USAGE 1.
    // explicite cast must exist in call to this function
    // and clearly states it will be readonly inside TestCalled function.
    TestCalled(a);                  // invalid call, we must explicit cast to ReadOnly<T>
    TestCalled((ReadOnly<long>)a);  // explicit cast to ReadOnly<T>

    // CALL USAGE 2.
    // Debug vs Release call has no difference - no compiler errors
    TestCalled2(a);

  }

  // ARG USAGE 1.
  static void TestCalled(ReadOnly<long> a)
  {
    // invalid operations, compiler errors
    a = 10L;
    a += 2L;
    a -= 2L;
    a *= 2L;
    a /= 2L;
    a++;
    a--;
    // valid operations
    long l;
    l = a + 2;
    l = a - 2;
    l = a * 2;
    l = a / 2;
    l = a ^ 2;
    l = a | 2;
    l = a & 2;
    l = a << 2;
    l = a >> 2;
    l = ~a;
  }


  // ARG USAGE 2.
#if DEBUG
  static void TestCalled2(long a2_writable)
  {
    ReadOnly<long> a = new ReadOnly<long>(a2_writable);
#else
  static void TestCalled2(long a)
  {
#endif
    // invalid operations
    // compiler will have errors in debug configuration
    // compiler will compile in release
    a = 10L;
    a += 2L;
    a -= 2L;
    a *= 2L;
    a /= 2L;
    a++;
    a--;
    // valid operations
    // compiler will compile in both, debug and release configurations
    long l;
    l = a + 2;
    l = a - 2;
    l = a * 2;
    l = a / 2;
    l = a ^ 2;
    l = a | 2;
    l = a & 2;
    l = a << 2;
    l = a >> 2;
    l = ~a;
  }

}
SoLaR
  • 778
  • 7
  • 22
  • It would be better if ReadOnly would be struct and VT could be both struct and class, that way if VT is struct then value will be passed by value and if it's class then value will be passed by reference and if you want it to be like java's final the operator ReadOnly should be implicit. – Tomer Wolberg Oct 09 '18 at 14:03
  • Totally forgot about this. Yes you are right struct and allowing VT to be any would be nicer solution. Now i recall left explicit on purpose to do not leave it in code, ie: when you have long math expressions with lower and upper case characters you may end up mixing them, this would help discover mixture, but it is performance down. Boxed solution no 2 is transparent to this explicit cast. But don't recommend to no one using this style of coding, also today we have C# 7.2 and 'in' keyword (as Max presented) that should satisfy this. – SoLaR Jun 07 '21 at 06:02
2

If you often run into trouble like this then you should consider "apps hungarian". The good kind, as opposed to the bad kind. While this doesn't normally tries to express constant-ness of a method parameter (that's just too unusual), there is certainly nothing that stops you from tacking an extra "c" before the identifier name.

To all those aching to slam the downvote button now, please read the opinions of these luminaries on the topic:

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Note that you don't need a naming convention to enforce this; you could always do it use an analysis tool after the fact (not great, but nevertheless). I believe Gendarme (http://www.mono-project.com/Gendarme) has a rule for it, and probably StyleCop/FxCop too. – Noon Silk Feb 26 '10 at 03:08
  • Easily the worst attempt (failed) at a solution. So now your parameter is not only writable, it's setting you up for failure by lying to you about it not having changed. – Rick O'Shea Apr 20 '18 at 16:22
0

If struct is passed into a method, unless it's passed by ref, it will not be changed by the method it's passed into. So in that sense, yes.

Can you create a parameter whose value can't be assigned within the method or whose properties cannot be set while within the method? No. You cannot prevent the value from being assigned within the method, but you can prevent it's properties from being set by creating an immutable type.

The question isn't whether the parameter or it's properties can be assigned to within the method. The question is what it will be when the method exits.

The only time any outside data is going to be altered is if you pass a class in and change one of it's properties, or if you pass a value by using the ref keyword. The situation you've outlined does neither.

David Morton
  • 16,338
  • 3
  • 63
  • 73
  • I would +1 except for the comment about immutability...having an immutable type wouldn't accomplish what he's looking for. – Adam Robinson Feb 26 '10 at 02:19
  • Sure it would, by default. An immutable type, not passed by reference, will ensure that the value, outside the method, doesn't change. – David Morton Feb 26 '10 at 02:21
  • 1
    True, but his example is about changing the value *inside* the method. In his example code, x is an Int32, which is immutable, but he's still allowed to write `x++`. That is, he's trying to prevent reassignment of the parameter, which is orthogonal to mutability of the parameter's value. – itowlson Feb 26 '10 at 02:31
  • I just re-read my post. My wording is confusing. I took that line out completely. – David Morton Feb 26 '10 at 02:45
  • I downvoted you for misunderstanding the question; it's not about changing the value AFTER the call, but changing it WITHIN it. – Noon Silk Feb 26 '10 at 03:03
  • 1
    @silky Kinda strange to downvote three answers that misunderstood the same question. Perhaps it's not the reader's fault the question was misunderstood. You know, when a man divorces his third wife, it's typically not the wives' fault. – David Morton Feb 26 '10 at 03:15
  • 2
    @David: It's the purpose of the voting system. To order by relevance. I don't see why it should concern you to be corrected. – Noon Silk Feb 26 '10 at 03:17
  • @David @silky I think Rosarch gave a bad example case, because integers are pass by value, and with that the original intention of the question became a little opaque. Hence my original comment about the Java final keyword. – Corey Sunwold Feb 26 '10 at 03:19
  • @Corey I found it trivial to follow, but that's because I've got a background in Java. I don't see why anyone should be upset about being wrong. It just shows that the question needs to be read carefully, and that you shouldn't be so fast to answer. But nevertheless, I don't think David will accept my comments. It's fairly meaningless to me, though, as I was simply providing a reason for the downvote, and trying to follow the rules of the (IMHO, significantly wrong) voting system. – Noon Silk Feb 26 '10 at 03:22
0

The recommended (well, by me) is to use an interface that provides read only access to the members. Remembering that if the "real" member is a reference type, then only provide access to an interface supporting read operations for that type -- recursing down the entire object hierarchy.

David V. Corbin
  • 344
  • 1
  • 10