参数可以是常数吗?

我正在寻找 Java 的 final的 C # 等价物。它存在吗?

C # 是否有以下内容:

public Foo(final int bar);

在上面的示例中,bar是一个只读变量,不能被 Foo()更改。在 C # 中有什么方法可以做到这一点吗?

例如,可能我有一个长方法,它将处理某些对象(int)的 xyz坐标。我想要完全确定这个函数不会以任何方式改变这些值,从而破坏数据。因此,我想声明它们为 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.
}
52508 次浏览

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.

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.

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.

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:

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 :-)

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#.

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.

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;
}


}

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.

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.