(Note: this proposal was briefly discussed in #98, the C# design notes for Jan 21, 2015. It has not been updated based on the discussion that's already occurred on that thread.)
Today, the ‘readonly’ keyword can be applied to fields; this has the effect of ensuring that a field can only be written to during construction (static construction in the case of a static field, or instance construction in the case of an instance field), which helps developers avoid mistakes by accidentally overwriting state which should not be modified. Optimizations are also possible in a compiler based on the knowledge that the field is immutable after construction.
Here’s an example of a class defined with a readonly field. The ‘m_birthDay’ field is explicitly declared by the developer to be readonly, which means any attempt to set it after construction will cause a compile-time error. The ‘FirstName’ and ‘LastName’ properties, which are defined with getter-only auto-props (introduced in C# 6), also result in readonly fields generated implicitly by the compiler, since there’s no need for a field to be writable if there’s no way for code to set it.
``` C#
public class Person
{
readonly DateTimeOffset m_birthDay; // readonly field, assigned in constructor
public Person(string firstName, string lastName, DateTimeOffset birthDay)
{
FirstName = firstName;
LastName = lastName;
m_birthDay = birthDay;
}
public string FirstName { get; } // getter-only auto-prop, backed by readonly field
public string LastName { get; }
public TimeSpan Age => DateTime.UtcNow – BirthDay;
public string FullName => "\{FirstName} \{LastName}";
public DateTime BirthDay
{
get => m_birthDay;
set => m_birthDay = value; // Error: can’t assign to readonly field outside of ctor
}
}
## Problem
Fields aren’t the only places developers want to ensure that values aren’t mutated. In particular, it’s common to create a local variable to store temporary state, and accidentally updating that temporary state can result in erroneous calculations and other such bugs, especially when such "locals" are captured in lambdas.
## Solution: readonly locals
Locals should be annotatable as ‘readonly’ as well, with the compiler ensuring that they’re only set at the time of declaration (certain locals in C# are already implicitly readonly, such as the iteration variable in a ‘foreach’ loop or the used variable in a ‘using’ block, but currently a developer has no ability to mark other locals as ‘readonly’).
``` C#
readonly long maxBytesToDelete = (stream.LimitBytes - stream.MaxBytes) / 10;
...
maxBytesToDelete = 0; // Error: can’t assign to readonly locals outside of declaration
This is particularly valuable when working with lambdas and closures. When an anonymous method or lambda accesses local state from the enclosing scope, that state is captured into a closure by the compiler, which is represented by a “display class.” Each “local” that’s captured is a field in this class, yet because the compiler is generating this field on your behalf, you have no opportunity to annotate it as ‘readonly’ in order to prevent the lambda from erroneously writing to the “local” (in quotes because it’s really not a local, at least not in the resulting MSIL). With 'readonly' locals, the compiler can prevent the lambda from writing to local, which is particularly valuable in scenarios involving multithreading where an erroneous write could result in a dangerous but rare and hard-to-find concurrency bug.
``` C#
readonly long index = …;
Parallel.ForEach(data, item => {
T element = item[index];
index = 0; // Error: can’t assign to readonly locals outside of declaration
});
## Solution: readonly parameters
As a special form of local, parameters should also be annotatable as 'readonly'. This would have no effect on what the caller of the method is able to pass to the parameter (just as there’s no constraint on what values may be stored into a ‘readonly’ field), but as with any ‘readonly’ local, the compiler would prohibit code from writing to the parameter after declaration, which means the body of the method is prohibited from writing to the parameter.
``` C#
public void Update(readonly int index = 0) // Default values are ok though not required
{
…
index = 0; // Error: can’t assign to readonly parameters
…
}
This support for ‘readonly’ parameters would be particularly helpful for a very specific usage of parameters: passing structs by reference. When a reference type is passed to a method, the state that’s actually passed into the method is a copy of the reference to the object (the reference is passed by value), effectively a pointer-sized piece of data. In contrast, when a struct is passed into a method, a copy of the struct is passed in (the struct’s state is passed by value). If the struct is small, such as is an Int32, this is perfectly fine and there’s little better that could be done by the developer. However, when the struct is large (for example, the System.Windows.Media.Media3D.Matrix3D struct contains 16 doubles, making it 128 bytes in size), it can become quite expensive to continually pass around copies of the data. In these cases, developers often resort to passing structs by ref, passing a pointer to the original data rather than making a copy. This avoids the performance overhead, but it now inadvertenly enables the called method to update the original struct’s value:
``` C#
public void Use(ref Matrix3D matrix)
{
…
matrix.M31 = 0; // Oops! We wanted performance, not more bugs.
…
}
By enabling marking parameters ‘readonly’, the struct could be passed by reference but still prevent the callee from mutating the original value:
``` C#
public void Use(readonly ref Matrix3D matrix)
{
…
matrix.M31 = 0; // Error: can’t assign to readonly parameters
…
}
This would also enable readonly struct fields to be passed by ref (to a readonly ref parameter), whereas in C# today passing a readonly struct field by ref is never allowed.
As with ‘readonly’ on fields, ‘readonly’ as it applies to locals and parameters would be shallow rather than deep, meaning that it would prohibit writing to the readonly field, but it wouldn't prevent mutation of any state contained in objects referenced from the ‘readonly’ value.
In many situations, in particular when "var" is used to declare a local with type inference, the developer often would like 'readonly' behavior for that local. This is possible if 'readonly' is allowed on locals, e.g.
``` C#
readonly int foo = ...;
readonly var bar = ...;
but it makes the desired immutability harder to express than mutability. To combat that, as shorthand for 'readonly var', we could also introduce a new 'let' or 'val' syntax, with the same meaning as 'readonly var':
``` C#
val foo = ...;
val bar = ...;
I'd like to allow the readonly
modifier on classes to allow immutability by default, and methods also to correlate with C++ const
functions.
``` C#
readonly class Point
{
public int x; // Implicit readonly
public int y; // Implicit readonly
Point(int x, int y)
{
this.x = x;
this.y = y;
}
}
Point origin = new Point(0, 0);
origin.x = 1; // Error: can't reassign a readonly class member
```
readonly
always felt a little bit of a short sell for instances of types with mutable internals. The readonly-ness is much more valuable when transitive, as happens with (well behaved) C++ const
values.
``` C#
class C
{
private int _i;
public int Read() readonly { return _i; } // method has 'readonly' modifier
public void Write() { _i++; }
}
readonly C c = new C();
c.Read();
c.Write(); // compile error: method is not readonly
The `readonly` modifier restricts the method's ability to modify its own members. Any members are also treated as `readonly` too, and only their `readonly` methods may be invoked. The entire graph of references from this point outwards becomes immutable.
---
As functional programming paradigms become more commonplace, function purity becomes an comforting property to have, and an appealing property to enforce. Today we have [`[Pure]`](https://msdn.microsoft.com/en-us/library/system.diagnostics.contracts.pureattribute%28v=vs.110%29.aspx), but there is no guarantee made by the compiler that this attribute is used factually. A strict purity constraint would extend the above readonly guarantee to the method's arguments any any static values/references. Such a function could have no observable side effects and would be truly idempotent. A `pure` method modifier could exist that extends this immutability not only to `this` but to all parameters.
``` C#
void PureFunction(C c) pure // method has the 'pure' modifier
{
c.Read();
c.Write(); // compile error: a pure function's parameters are implicitly readonly
}
Framework code could optimise for or even require pure functions. The compiler could automatically hoist loop invariant code.
Re parameters, I don't like the way that readonly
on reference parameters pollutes the (in-source) signature with information that is irrelevant to the caller.
For structs, it would be nice to have those semantics - I've often wished for in
as an addition to ref
and out
, as readonly ref
is not very symmetrical :grin: It wasn't mentioned in the proposal, but the call site should not require annotation with in
/ref
.
@drewnoakes I completely agree, but backwards compatibility means we're screwed.
C#
readonly var list = new ArrayList<int>();
readonly var size = list.size(); // Error, calling non-readonly method on readonly reference.
@stephentoub The following example is ambigous:
public void Use(readonly ref Matrix3D matrix) { … matrix.M31 = 0; // Error: can’t assign to readonly parameters … }
This would also enable readonly struct fields to be passed by ref (to a readonly ref parameter), whereas in C# today passing a readonly struct field by ref is never allowed.
As with ‘readonly’ on fields, ‘readonly’ as it applies to locals and parameters would be shallow rather than deep, meaning that it would prohibit writing to the readonly field, but it wouldn't prevent mutation of any state contained in objects referenced from the ‘readonly’ value.
The first thing that I thought, when I saw readonly ref
was, that ref
was used to avoid a copy and readonly
was given, to avoid that the Use
method changes it. Then I realized, that readonly
was used to declare the underling structure as const
.
My proposal to resolve this ambiguity is:
public void Use(readonly ref const Matrix3D matrix)
{
…
matrix = new Matrix3D(); // Error: can't assign to readonly reference
matrix.M31 = 0; // Error: can’t change immutable value
…
}
The variable is readonly and the Matrix3D
value is immutable. It should also be possible to use the following syntax:
public void Use(ref const Matrix3D matrix)
{
…
matrix = new Matrix3D(); // OK
matrix.M31 = 0; // Error: can’t change immutable value
…
}
const
objectsCalling methods on const
objects should only be allowed when those methods don't make changes to the underlying object.
The following shouldn't work:
class TestObject {
public int i;
public void SetValueOfI(int v) {
i = v;
}
}
public void Use(ref readonly TestObject o)
{
…
o.SetValueOfI(0); // Error: can’t call non-const method on const object
…
}
Instead we have to be able to make methods and properties (setter/getter) readonly too:
class TestObject {
public int i;
public int AddWithI(int v) const {
return i + v;
}
}
public void Test()
{
var o = new TestObject { i = 1 };
Use(ref o);
}
public void Use(ref readonly TestObject o)
{
…
Assert.AreEqual(124, o.AddWithI(123)); // OK: Can call AddWithI, because it doesn't change the underlying object
…
}
I personally prefer const
over readonly
, because IMHO readonly
means, that the variable can only be read, but this doesn't necessarily apply to the object it points to (similar to C's T const * const
) and - to me - immutable variables and immutable values are different things.
@ryancerium, I suppose this feature would require new metadata in the type system, therefore a new CLR and at the same time a new BCL. ArrayList<T>
and friends would be annotated accordingly.
@ryancerium can you further explore why the readonly
modifier should apply to the type rather than the instance? The latter seems more flexible. You could still have:
C#
readonly Point origin = new Point(0, 0);
origin.x = 1; // compile error: can't modify a readonly instance's fields
@drewnoakes readonly
isn't transitive for fields, or for arrays for that matter. It was a mistake committed long ago to not have transitive readonly
like C++ const
, but it's where we are. Don't get me wrong, I much prefer it, but I think that ship has sailed.
I want to co-opt readonly
to make it easier to make immutable data types. Right now I have to put readonly
on all the data members, and that's annoying as hell :-)
Here's a different Point class that has mutable x and y values.
``` C#
class Point
{
public int x, y;
}
class C
{
readonly Point origin = new Point(0, 0);
public void M(readonly Point p)
{
origin.x = 100; // Totally fine in C# as-is today.
p.x = 100; // Error per your suggestion, seems weird when I can modify origin
readonly Point d = new Point(p.x - origin.x, p.y - origin.y);
d.y = 100; // Error per your suggestion, seems weird when I can modify origin
// d = new Point(-1, -1); // Illegal today, reassigning the reference
}
}
```
Agreed on the desire for readonly classes, it would be very much appreciated.
I think the syntax for local readonly values should take a leaf out of the F# book and use let
. The reason for that is I think val
looks too close to var
and would make it slightly less easy to scan code quickly. And I think trying to avoid a readonly
prefix would be wise to reduce the amount of effort required to 'do the right thing' in choosing the immutable option when coding. As somebody who likes to make as much of my code as pure and as immutable as possible, I would much prefer not to have to litter my code with readonly
.
val foo = 123;
var bar = 123;
Compared to:
let foo = 123;
var bar = 123;
I think the second version is much more 'scannable'.
The devil is always in the details on these kinds of proposals around immutability, const, etc. (i.e. when you start working through the intricacies, there are a variety of interesting gotchas and corner cases that need to be handled and increase complexity or at least acknowledged that there are holes.) I have several more proposals to post in this area, I just didn't get a chance to do so yesterday; I'll ensure they're linked to from here when I put them up, hopefully later today.
And I think trying to avoid a readonly prefix would be wise to reduce the amount of effort required to 'do the right' thing in choosing the immutable option when coding.
As somebody who also programs in Java which has final
for readonly parameters and locals as well as fields, I always use it (when applicable) for fields but I often omit it from parameters and locals. I think let
would be a good choice, and I would definitely use it.
The devil is always in the details on these kinds of proposals around immutability
@stephentoub Ain't that the truth. Is there a (very large) matrix anywhere that combines the list of types and methods and object locations (parameter/field/local/static)? Or do the language designers just keep them in the back of their head at all times?
``` C#
struct S{}
struct
struct
class C{}
class
class
dynamic D{}
static class
{
int a;
S
```
Ah forget it, I'm getting tired already and realizing that the list is longer than War and Peace.
I like this proposal, especially around using let
to infer a readonly local. This makes the need to actually type the word readonly
in the context of a method very rare, which is a good thing.
I fully agree that let
is the preferred syntax here, at least for local readonly variables / values. I believe that programmers should be encouraged into programming with immutability in mind, and having a nice, short keyword will facilitate this :).
Of course, let would work with type inference; you could simply swap var
for let
in most programs and you'd be fine. If you want to explicitly type the variable then you can much the same as in linq query expressions. So you should be able to write
let john = "John"
or let string john = "John"
the same way in Linq you can write
from name in names
and from string name in names
.
This also has the added benefit of matching both F# and Swift for syntax.
@Richiban: let john : string = "John"
would be the correct F# syntax. let string john = "John"
would declare a method called string
, taking a parameter john
of generic type, returning the constant "John"
.
However, I also agree that let
should be preferred over val
. The syntactic difference between val
and var
is just too small and easy to miss.
@axel-habermaier Thanks, I was aware of the correct F# syntax. Perhaps I wasn't clear - let string john = "John"
is what I want the C# syntax to be.
The "let" keyword would not read well for parameters.
Is it possible for the compiler to determine that a struct is not modified in a method? If so, the struct could then be passed by ref to that method as a compile-time optimization. Obviously a readonly
keyword would be a good clue that a struct should be passed by reference into that method. I don't see that you need both the readonly
and ref
keywords together.
@gafter
The "let" keyword would not read well for parameters.
How do you mean?
My two cents?
Support readonly
as a modifier keyword for both parameters and local variables. Parameters with this modifier cannot be modified by the method nor can they be passed to methods as ref
or out
parameters. Local variables with this modifier must have an initializer and also cannot be modified by the method nor passed to other methods as ref
or out
parameters.
The behavior of this modifier will be exactly like the behavior of the readonly
modifier when applied to fields. If the value of the variable is a reference type then any state carried by the instance may be modified through whatever members are exposed by that type, including public fields. If the value of the variable is a struct or value type then members which may mutate the value cannot override the value stored in the variable.
public class Box<T> {
public T Value;
}
public struct Incrementor {
private int value;
public Incrementor(int initialValue) {
this.value = initialValue;
}
public int Increment() {
this.value += 1;
return this.value;
}
}
public void Foo(Incrementor i1, readonly Incrementor i2, readonly Box<string> boxed) {
Debug.Assert(i1.Increment() == 1);
Debug.Assert(i1.Increment() == 2);
Debug.Assert(i1.Increment() == 3);
Debug.Assert(i2.Increment() == 1);
Debug.Assert(i2.Increment() == 1);
Debug.Assert(i2.Increment() == 1);
readonly Incrementor i3 = new Incrementor(0);
Debug.Assert(i3.Increment() == 1);
Debug.Assert(i3.Increment() == 1);
Debug.Assert(i3.Increment() == 1);
i3 = new Incrementor(5); // compiler error, i3 is readonly
boxed.Value = "foo";
Debug.Assert(boxed.Value == "foo");
boxed.Value = "bar";
Debug.Assert(boxed.Value == "bar");
boxed = new Box<string>(); // compiler error, boxed is readonly
readonly int number = int.Parse("4");
int.TryParse("5", out number); // compiler error, number is readonly
}
Foo(new Incrementor(0), new Incrementor(0), new Box<string>() { Value = "baz" });
Additionally, as a counterpart to var
also support the use of the existing let
keyword as a method to declare implicitly-typed readonly variables. No type is permitted to be specified. This is syntactically similar to how let
is already used in LINQ queries.
var i = 2;
let j = 3;
i = 4;
j = 5; // compiler error, j is readonly
Apart from the existing similar usage of let
in C# it is also used by Apple Swift to define readonly variables, although that language does optionally permit explicitly specifying the type.
I could get down with this syntax - if it's technically the addition of the readonly keyword but locals have the keyword let
as shorthand for readonly var
then you get the best of both worlds as far as I'm concerned.
The only thing I'm concerned about is the fact that requiring an extra keyword on method parameters will mean that nobody bothers. They should be writing:
public int Add(readonly int x, readonly int y) {...}
but who's going to do that? Readonly parameters should be the default, but unfortunately that would be a breaking change and therefore has a close-to-zero chance of happening :(.
@Richiban
Readonly parameters should be the default, but unfortunately that would be a breaking change and therefore has a close-to-zero chance of happening :(.
Given that all parameters are by-val by default does that really matter? Let them screw with their version of x
and y
all they want, it won't affect the caller.
@HaloFour No, you're right, the caller will be fine. I'm thinking more of
reasoning the contents of a method. When variables are immutable by default
it's harder to write spaghetti code. I've often seen code like this in
existing projects:
public void DoSomething(Customer customer)
{
...
if (someCondition)
{
customer = new Customer();
}
...
}
This method is very hard to reason about (at least in the second half)
because you don't know whether customer
is still the object that was
passed into the method or not. That's what I mean by the fact that I would
prefer _all_ parameters to be readonly. Reusing the customer
parameter
name as if it were a local variable is sheer laziness on the part of the
programmer in this case, but if the language allows it and makes it easy
then unfotunately that's what people will do.
I've seen a lot of poorly written code but I've yet to see parameters modified in the middle of a method. Besides, there are valid cases to modify parameters:
C#
public void DoSomething(Customer customer)
{
customer = customer ?? new Customer();
...
You really don't want to introduce a new variable in this case because that means that someone my end up accidentally using the customer
parameter instead of using the new variable.
@Richiban If the method author wants to mess with the parameters and they were immutable by default, they would likely just add a mutate
keyword (or whatever this hypothetical keyword would be) to allow themselves to do so. I don't think making it the default would accomplish anything.
I imagine in practice very few methods will have parameters actually designated as readonly
. I think it only adds value on long methods and/or those with complex logic. And even then, its a weak guarantee as you can still write to properties or indexers of a readonly parameter.
Given they have such limited use, I'm not even sure readonly parameters are worth implementing at all... (Though I'm still a big fan of readonly locals).
@MgSam Perhaps you're right. I'm very much in the F# way of thinking, and in F# you can't declare mutable parameters at all. To me it doesn't really make any sense why you would want them any way.
However, while I'm all for the ability to add the readonly
keyword to method parameters (because "why not?") I think that in practice it will be rarely used since it's a) extra typing, b) extra noise to read c) of dubious value and d) will probably just lead to code style arguments amongst developers. Look at the final
keyword in Java. People should really use it (for reasons I think we all agree with) but the extra keyword makes a surprisingly large negative impact on readability and thus very few people use it. That's why I think it's so important that we have the keyword let
in C# for locals at least, because it's nice and short (val
would be my second choice).
Of course the major reason to use final
is the language requiring
parameters/variables be final
in order to reference them in an anonymous
class. No such concerns in C#. It would exclusively be for developers to
ensure that they don't accidentally overwrite the existing value.
On Feb 6, 2015 10:39 AM, "Richard Gibson" [email protected] wrote:
@MgSam https://github.com/MgSam Perhaps you're right. I'm very much in
the F# way of thinking, and in F# you can't declare mutable parameters at
all. To me it doesn't really make any sense why you would want them any way.However, while I'm all for the ability to add the readonly keyword to
method parameters (because "why not?") I think that in practice it will be
rarely used since it's a) extra typing, b) extra noise to read c) of
dubious value and d) will probably just lead to code style arguments
amongst developers. Look at the final keyword in Java. People should
really use it (for reasons I think we all agree with) but the extra keyword
makes a surprisingly large negative impact on readability and thus very few
people use it. That's why I think it's so important that we have the
keyword let in C# for locals at least, because it's nice and short (val
would be my second choice).—
Reply to this email directly or view it on GitHub
https://github.com/dotnet/roslyn/issues/115#issuecomment-73255483.
I agree with @Richiban: readonly
will in practice be applicable to most parameters, but won't be used because it's cumbersome to annotate every single parameter with it. It should be a default, but it's too late for that. Maybe as a project-wide option? VB had language options for years, C# not so much (except for arithmetic overflow checks).
Look at the previously mentioned Java's final
, or VB's ByVal
keyword (which isn't inserted by default anymore since VS2010 SP1 because it was too verbose).
@MgSam
I think it only adds value on long methods and/or those with complex logic
If you're smart enough to understand that your method is too complex and might need a readonly
parameter, you're also probably smart enough to refactor it :) Corollary: you won't find readonly in code where it's really needed.
That said, I'm all for readonly locals, with a simple short keyword (my vote goes to let
).
I am very much in favor of adding readonly local declarations. However, I would like to suggest that the keyword which is chosen to introduce a readonly, type inferred local be val
and not let
. The reason is that I feel it makes the method body read better and provides a nice symmetry with the existing var
keyword.
To illustrate this, let us consider the let
keyword (pun intended), which reads extremely well in the context of a query because it is used as a verb.
Consider:
``` c#
evenSquares =
from n in numbers // from is a preposition
let nSquared = n * n // let is a verb
where n % 2 == 0 // where is a preposition
select nSquared; // select is a verb
This reads wonderfully and is very symmetric.
Now let us consider the use of the `var` keyword, which can be thought of as a noun or noun modifier.
Consider:
``` c#
int ComputeSomething (IEnumerable<int> numbers) // C# 6.0
{
var result = 0; // var is a noun or noun modifier
var offset = 25; // this is not going to be modified
foreach (var n in numbers) // var is a noun or noun modifier
{
if (n % 2 == 0)
{
result += offset;
}
result += n;
}
return result; // return is a verb
}
This reads very nicely.
Consider:
``` c#
int ComputeSomething (IEnumerable
{
var result = 0; // var is a noun or noun modifier
let offset = 25; // let is a verb
foreach (let n in numbers) // let is what?
{
if (n % 2 == 0)
{
result += offset;
}
result += n;
}
return result; // return is a verb
}
This does not have the nice symmetry of the previous but the ability to express immutability is wonderful.
Now consider:
``` c#
int ComputeSomething (IEnumerable<int> numbers) // C# where val declares a readonly local.
{
var result = 0; // var is a noun or noun modifier
val offset = 25; // val is a noun or noun modifier
foreach (val n in numbers) // val is a noun or noun modifier
{
if (n % 2 == 0)
{
result += offset;
}
result += n;
}
return result; // return is a verb
}
This the best by far.
I don't think that the fact that let
is a verb is that big of a deal. I think that you can read it more like binding the expression to a name in which case I think a verb is appropriate.
The two reasons that I don't care for val
is that it is yet another keyword (C# 7.0 feels like a keyword explosion thus far) and that it looks so similar to var
that when scanning through code you might not immediately notice the difference.
I like let
because it's already a keyword in C# that behaves in a nearly identical way and because it's not without precedent. Apple Swift uses var
and let
in virtually the same manner as this proposal. EcmaScript 6.0 has both keywords as well although their use is different. F# has val
and let
where val
is an unintialized declaration and let
is a binding, regardless of mutability.
That said, I don't feel all that strongly either way,
@HaloFour @louthy let
works well for local declarations, but it doesn't work so well in other contexts such as parameters where the initializing expression isn't given at the declaration:
public static void Main(let string[] args) // let does not read well here
{
}
@louthy I don't think there is a readability issue with var
vs val
. To the reader of the program they mean the same thing: declare a local variable. The only difference is to the person who modifies the code, as val
will place restrictions on what you can do.
I agree. let
would only be a counterpoint to var
for inferred
variables. This would also be true if yhry decide to use val
.
Parameters would just reuse the existing keyword readonly
, as could
explicitly declared variables.
On Feb 11, 2015 12:49 PM, "Neal Gafter" [email protected] wrote:
@HaloFour https://github.com/HaloFour @louthy
https://github.com/louthy let works well for local declarations, but it
doesn't work so well in other contexts such as parameters where the
initializing expression isn't given at the declaration:public static void Main(let string[] args) // let does not read well here { }
@louthy https://github.com/louthy I don't think there is a readability
issue with var vs val. To the reader of the program they mean the same
thing: declare a local variable. The only difference is to the person who
modifies the code, as val will place restrictions on what you can do.—
Reply to this email directly or view it on GitHub
https://github.com/dotnet/roslyn/issues/115#issuecomment-73927956.
@HaloFour so you suggest three keywords for the same concept? :/
Not three - 'readonly' is the keyword proposed, and 'readonly var' can be
shortened to 'let'.
On Wed, Feb 11, 2015 at 6:11 PM, Neal Gafter [email protected]
wrote:
@HaloFour https://github.com/HaloFour so you suggest three keywords for
the same concept? :/—
Reply to this email directly or view it on GitHub
https://github.com/dotnet/roslyn/issues/115#issuecomment-73932216.
@gafter Not at all. I would reuse the existing keywords readonly
and let
. The readonly
keyword could be applied to parameters or to formally declared variables:
public void Foo(readonly int x) {
x = 5; // compiler error
readonly int y = 10; // initializer required otherwise compiler error
y = 15; // compiler error
}
The let
contextual keyword would be used for inferred variables:
readonly int x = 10;
// equivalent to
let y = 10; // again, initializer required otherwise compiler error
y = 15; // compiler error
I vote for let
because C# already has the keyword with nearly identical syntax and behavior within LINQ projections, because it looks nothing like var
and would not be mistaken as such when scanning through code, and because other languages already offer it as a precedent. In my opinion if you consider let
to be a binding to an expression rather than a variable declaration it doesn't really matter that it is a verb.
I'm neither here nor there regarding readonly var
. It's the tiniest bit more verbose and seems unnecessary if let
or val
shorthand is provided.
So really, my "proposal" is about identical to the original proposal in this thread, other than putting in my vote for let
(over val
) and expanding a little on my preference that these variables behave very similarly to readonly fields.
@HaloFour There is also a precedent for the val
keyword in this context. Scala uses this syntax.
Most Scala IDEs use syntax colorization to help the reader distinguish between var
and val
.
In F# let
is used to bind readonly names to values inside function bodies, and let mutable
is used to bind non readonly names to values inside function bodies.
Anyway, I do read let
as binding a value to a name, which I think reads poorly in a context where var
also binds a value to a name.
For parameters, no matter it is readonly or val, it is nothing related to the signiture of the method. It is more about the method implementation. Think about interface and virtual methods.
So I think it may be better to be specified in method trait/contract.
@qrli I think that readonly
should be used in method signatures, property signatures, and in any place where the type must be stated explicitly. This is consistent with its existing use in field declarations. My suggestion is that val
be chosen as opposed to let
as the keyword for declaring readonly implicitly typed local variables. The reason is that it reads better and is more symmetric.
@aluanhaddad
If we are talking about "const" (as in C++) instead of "readonly", I will agree with you.
But for "readonly", it is nothing about the external visible behavior of a method, but only for the convenience of a specific implementation. Enforcing it on a type system provides only trouble.
@qrli I think we agree.
(sorry, but I haven't read all the comments with the required and deserved attention)
I do agree that having unmodifiable locals is very useful. For closures and code correctness.
But I think, like with fields, there are to use cases that could be leveraged: readonly and const locals.
A readonly local would be pretty much how @stephentoub specified.
A const local would be like a field const. const locals would be replaced by its value like the field equivalent.
As to syntax, I wonder if only the modifier could be used:
readonly x = GetX();
const y = 1;
var z = GetZ();
It's a bit of a stretch, I know.
As for function parameters, taking the @stephentoub's use case of large structs, if I mark a by referenece parameter as readonly, I can't pass it along by reference without the assurance that it won't be modified. So the readonlyness must be surfaced to the caller. The suggestion of an in
modifier seems like a good idea here. It will also give space for the combined use of in
and readonly
if such a use case surfaces.
And, wouldn't it be great if I could mimick the behavior of foreach
and using
?
var a = 1;
{
readonly a; // _a_ is readonly in this scope.
// use _a_ but don't use it.
}
a = 2; // _a_ is writable again.
Let me start by espousing my love for this entire concept. The more we can place intent into the code, the less likely we are to create bugs, the easier it is to share/consume code, and the more verification we can push upstream to the compiler. All good things.
const
'ness is always a delicate matter to design properly. The const
keyword in C was great in its day, but the languages derived from C have evolved to the point where const
isn't as flexible as it could be. C# has attempted to differentiate const
'ness with a variety of options: const
for compile time constants, readonly
for immutable allocations, and the System.Collections.Generic.IReadOnly*<T>
family. All good in their own right, but inconsistent as a whole.
This is a fantastic time to begin resolving the inconsistencies. For starters, in place of the IReadOnly*<T>
famlily, why not enable readonly to be a modifier of the type definition of generics?
example:
List<readonly T> list = List<readonly T>();
So what are the complete set of use cases?
Immutable parameter values:
void Method(readonly object Obj)
{
// ...
}
Immutable reference parameters:
void Method(readonly ref TimeSpan time)
{
// ...
}
Immutable allocations:
private readonly object;
private readonly object[] m_objs;
private readonly List<object>
immutable arrays:
private readonly char[] m_string = readonly { 'i', 'm', 'm', 'u', 't', 'i', 'b', 'l', 'e' };
Immutable generics:
public readonly List<readonly object> GetList { get { return ...; } }
Immutable local values:
for ( int i = 0; i < arr.Length; i++ )
{
let x = arr[i]; // let means readonly
...
}
Finally, I think we should always allow references to be cast towards less mutability as a rule.
List<object> foo = new List<object>();
List<readonly object> bar = foo; // legal, less mutable
readonly List<object> baz = foo; // legal, less mutable
readonly List<object> biz = bar; // illegal, as T would be more mutable
As mentioned on #3202, the one issue with readonly ref
when it comes to value types is that I don't think that there is any mechanism that would allow for the compiler to prevent calls to mutating methods. To the compiler they look like any other method defined on the value type.
static void Foo(readonly ref Point pt) {
pt.Offset(2, 2);
}
static void Main() {
var pt = new Point(2, 2);
Foo(ref pt);
// oops, pt is now {4, 4}
}
Lastly, I'd bring up the idea of a writable
keyword to allow partial undoing of readonly
. There's plenty of times when I'd like to make a reference readonly
but can't because current conventions are too strict.
Example, if for some reason (this is an actual use case for me currently) I need a fixed length dictionary where the keys of the dictionary are fixed (none should be removed or added) but users of the API are required to modify the values: today I need a custom type. With proper readonly
and writable
keyword usage I wouldn't.
public readonly Dictionary<readonly Control, writable Color> ControlColors { get { return ...; } }
@whoisj I don't see how your property definition defines a "read-only" dictionary. That seems to imply that the C# compiler would know a great deal more about the intent of the generic type arguments than it does. At best, the C# compiler could try to enforce that any parameter or return value of TKey
would be a readonly Control
, preventing use of property setters, but not much else. And since no such concept is baked into the CLR it couldn't be enforced at all.
@HaloFour hmm... I suppose you have a point.
It would be great if there was a way to create an IReadOnlyDictionary<TKey, TValue>
where the TValue
was writable without the need to produce a new type completely. Thoughts?
@ryancerium:
I want to co-opt readonly to make it easier to make immutable data types. Right now I have to put readonly on all the data members, and that's annoying as hell :-)
Have you changed your opinion on this after trying C# 6's truly readonly auto properties?
I think readonly on locals and parameters is a good thing.
I also really like the idea of being able to express that a method will not (and cannot) modify member fields and properties by applying the readonly keyword to methods.
On top of that, add a new modifier, immutable, that can also be applied to fields, locals, and parameters, that is different from readonly in that readonly specifies that the value of the field cannot be reassigned, while immutable specifies that the value (or referenced object) cannot be mutated.
The immutable keyword would add compiler checks that don't allow writing to fields, calling setters, or calling getters or methods not marked readonly (auto-properties would have implicit readonly getters). With the immutable keyword, you could effectively enforce immutability at the point where a type is used with a simple modifier, much like const in C++, but instead of the confusing const * const syntax, you could instead combine the readonly and immutable keywords. So "public readonly immutable MyType MyField" would be completely valid.
Essentially, to enforce the rules of a readonly method, the method body of a readonly method would only have access to an "immutable this" reference rather a normal, mutable "this".
This also gives the added benefit of possible runtime optimizations since you could safely pass value types to parameters marked readonly immutable by reference.
For example:
public class Person
{
public string FirstName { get; set; } // Implicit readonly get
public string LastName { get; set; }
public void ChangeMe() { }
public readonly string GetFullName { return FirstName + " " + LastName; }
}
public void SetPerson(Person person)
{
person.FirstName = "John";
person.LastName = "Doe";
}
public void WritePerson(immutable Person person)
{
Console.WriteLine(person.FirstName); OK - calling a readonly getter is fine
Console.WriteLine(person.GetFullName()); // OK - GetFullName is marked readonly
person.FirstName = "Bill"; // Error - Can't call a setter on an immutable reference
person.ChangeMe(): // Error - ChangeMe is not marked readonly
SetPerson(person); // Error - can't pass an immutable reference to a method that takes a mutable reference
}
It gets a little more complicated when you have an immutable reference to an object that has a property of a complex type. To ensure immutability, the compiler would have to assume that all values obtained from an immutable reference, whether returned by field access, getters, or method calls are also immutable. There may be a need to override this for some methods (think factory methods) so a way to reverse the immutability may be in order (mutable would be an obvious keyword choice):
public class PersonFactory
{
public mutable Person CreatePerson() { ... } // The person returned from this method would always be mutable, even if the reference to the PersonFactory is an immutable reference
}
Also, the immutable keyword would be a valid modifier for return types:
public immutable Person GetPerson(int id) { ... }
@binki This proposal only refers to the variables/parameters themselves, not to the references to which they point. If the parameter was of type readonly ref Point
you'd still be able to mutate the struct via calling one of it's mutating methods like Offset
.
You should probably look into #159, a proposal for immutable types.
Ah, sorry. I was just looking at @jonathanmarston’s post which seems to be a proposal to change the definition of readonly
to support const *
semantics.
You should probably look into #159, a proposal for immutable types.
Hmm. It is hard to tell where my thoughts belong. That proposal seems, at least initially, to be more about building classes which are immutable than implementing const
in C#. Bother, I don’t know if I have time to figure out even the right venue to place my idea :-/.
I do agree with the main tenant of this issue: that locals and parameters should be able to be declared readonly
. This would be useful both when accepting structs as ref parameters and to control the mutability of fields in lambda capture implicitly-created objects. Even without these features, it can enable the programmer to protect him/herself from accidental mutation of variables that should only be set once. However, this issue is then also not a place to discuss changes to what readonly
means. C# already has a well-accepted (if broken) meaning for readonly
, and the changes that @jonathanmarston and I suggest should be considered separately.
@binki Could always start a new proposal. :)
I think the problem is that from C# it can be difficult to tell what does mutate state and even if you could you wouldn't be able to prevent those methods from mutating state, only prevent calls to those methods. That's why I mentioned ref Point
as to C#'s point of view there is nothing about calling Offset
that provides a clue to the compiler that the underlying Point
will be modified. There would need to be work beyond compiler features to enable guarding against such changes at compile-time.
"readonly for locals" would be a "free feature" of #6400. But it doesn't help with parameters.
I also would really like to use readonly
(and let
) in local scope. But in my opinion readonly
should have the same consequences on local variables and parameters as it has on fields.
readonly MyStruct x;
x.Func();
Here an implicit copy of x will be created to call Func()
. That's how readonly
fields work today [1]. Therefore I don't see how a readonly ref
parameter will have a positive effect on the reduction of structure copies. Nearly any access to the passed structure reference will create an implicit local copy.
So I vote for readonly
locals, but against readonly
parameters.
[1] http://blogs.msdn.com/b/ericlippert/archive/2008/05/14/mutating-readonly-structs.aspx
@gafter using let
to declare local variables reads well in the pattern context of your example:
``` C#
let (int x, int y) = ComputePoint();
but it reads very poorly as the standalone declaration of an implicitely typed readonly local variable:
``` C#
let x = 5;
looks ok out of context, but in the broader syntactic context it looks very out of place:
``` C#
var radius = 2;
let pi = 3.14159265359;
var area = pi * Pow(radius, 2);
is awkward but passable.
However, it starts to look really weird in some constructs:
``` C#
foreach (let customer in customers)
{
...
}
And if it were adopted as a parameter modifier, then things really read awkwardly:
``` C#
bool UserHasAccess(let UserRole userRole, let bool lockDownInEffect, let Request request) {...}
I know Swift does this, and it reads very poorly there as well, so I don't think that legitimizes it.
ES6 also uses let, with a different meaning, and it reads poorly their as well
``` JavaScript
for (let value of values) {...}
I know it's just syntax but I really think another keyword would be better. My preference would be for val
but even the more verbose readonly
would be better than let
.
I think let
_does_ read well in LINQ expressions because of the alternating verb preposition verb flow of the LINQ keywords, but it looks out of place in a lot of imperative situations and downright silly as a parameter qualifier.
Edit: I realize I am reiterating previous remarks made by @gafter, @MadsTorgersen, and others, as well as repeating myself, but I think it's worth bringing the "does it read well" concern up again in light of the subsuming work on #6400.
Edit: accidentally submitted before I was finished.
@aluanhaddad There is no proposal to embed the let
statement inside the parens of a foreach
loop. I don't know why you would want that, given that a foreach
loop control variable is already readonly. Nor would we likely use it as a parameter modifier.
See also https://github.com/dotnet/roslyn/issues/115#issuecomment-72690182 https://github.com/dotnet/roslyn/issues/115#issuecomment-73927956 and https://github.com/dotnet/roslyn/issues/115#issuecomment-156122915 where I keep saying that to anyone who will listen. If we want to add support for readonly parameters, I expect we would use the existing keyword readonly
.
However for local variables I believe let
reads very nicely, _especially_ aside the use of var
for mutable locals as in your examples. I can imagine also supporting readonly
as a modifier for locals if we allow it for parameters, though I do not imagine it would be used much given the more concise let
.
@gafter I know that foreach iteration variables are readonly, but if let
is introduced with "read only local variable declaration" as one of its meanings then I think it would be a bit odd if let declarations were illegal in that context. Furthermore, if let
is used for destructuring assignment, and if destructuring assignment is ever permitted in the declaration cause of the foreach statement, then the inconsistency will be increased.
As for readonly parameters, I see that my example was erroneous.
I am thinking a readonly modifier would be useful for allowing the compiler to inline methods. Methods that accept structs as parameters can not be inlined and I suspect that is because when you pass a struct to a method, you can not change the original, but if the method were inlined, you could, and that might have serious impacts on the behavior of the code.
If you could tell the compiler that the parameter is not going to modified by the method, the compiler would gain the option to inline it, and that would have performance benefits.
@mcgolledge I don't think that would matter as the readonly
would only be a compiled-enforced directive and not part of the metadata of the method nor enforced by the CLR. Furthermore, the readonly
modifier would only prevent reassignment of the variable, it would not prevent mutating the variable through assigning a value to a property or calling a mutating method.
public void Foo(readonly Point p) {
p.X = 5; // legal
p.Offset(2, 2); // legal
p = new Point(0, 0); // compiler error
}
Methods that accept structs as parameters can not be inlined
Actually they can be inlined and current JIT compilers do so.
Thanks, I was a little out of date on JIT compilers. It looks like the ability to inline methods with value type parameters was introduced somewhere around 3.5.
@HaloFour , it's moot now, but I think your argument would depend on if Point and its members were classes or structs. It's just the old C-style const pointer const var problem, where you have to remember to make the pointer and what is stored at the pointer readonly or you haven't bought yourself much. And, there are always ways to circumvent that if you want to write malevolent code.
@mcgolledge I mentioned Point
specifically because it is a struct. The CLR provides no metadata that a struct method might mutate the value of the struct in place and there isn't really anything that the compiler could do about it short of copying the struct to a temporary variable before attempting to call any methods on it.
But yes, it is moot for your point. It's just an issue with readonly
and structs in general.
readonly
just seems really wordy for my taste. I'd rather just use let
for locals. There isn't any situation when the type cannot be inferred at the point of deceleration, so types need not be specified at all. Personally, I always define locals with var
anyway.
I'm not against using readonly
for parameters, but it's not very important for me.
Also, it would be nice if re-declaring locals would be allowed, such as:
let n = 5;
let n = 6;
With the latter declarations making the previous ones inaccessible. Note that after declaring a value with let
, simply typing n = 5;
would be illegal of course.
I'm strongly against const-correctness of any kind in C#. This is largely due to compatibility concerns. Const correctness is something that has to be part of the language since day 1 to work properly when using different APIs. For this reason, even if the feature were available, I would only ever use it in very narrow scopes or maybe even not at all.
We, as programmers, are so used to the term variable
that nobody has realized/stated here that the term readonly var
is actually an oxymoron: if something is "variable", then, it cannot be read-only.
So yeah, I also support those calling for let
to be used in this case. Especially because it goes closer to the idea of immutability-by-default. It could also be allowed later at the class level, and everybody would stop using 'var' for everything, only in the rare case when mutability is needed. (For this reason I don't think taking the readonly
to be allowed in more places is a good idea. We should find new ways to express immutability without the need of extra qualifiers.)
@knocte It's been discussed for type inferred const
as well (#4423). While let
(#6400) has an specific form just for this purpose, according to the #115 (comment) there is a chance to support also readonly
for read-only locals. As I said, I think an optional type would be more consistent instead of something like readonly var
.
First. I would like to use const
instead of readonly
Second. I think it better for const
tobe shorthand for readonly ref
. If it marked as const it should pass by ref immediately
Third. I go against let
the most. let
is keyword only for query expression. It should not be used anywhere outside. If the keyword should be reused it should let bigger scope reuse in deeper scope, not the other way around
Problem is like, value
keyword was used only inside set { }
. So we may have variable named value everywhere in other function. So as let
Finally. const
should also be shorthand for const var. Because it always need to set immediately so if we write const i = 1;
It must know i
is int
What is the state of this proposal?
First. I would like to use
const
instead ofreadonly
The keyword const
infers complete immutability and "const correctness". The readonly
keywords means the references cannot be updated, but implies nothing about the values on the other end of the reference. For value types this makes them immutable (ignoring unsafe), for reference types all bets are off.
I'd like to see const
parameters as well, so
class VersionString {
public string Verison { get; }
public VersionString(const string version) {
if(!Regex.IsMatch(...)) throw new ...;
this.Version = version;
}
public static implicit operator VersionString(const string str) {
return new VersionString(str);
}
}
VersionString version = "1.0.";
causes a compile-time error rather than runtime.
@alrz That's asking for a lot more than const
parameters. That's asking for compile-time executed object literals. There's #263, #4971 and #5403 but this takes it a bit further by asking the compiler to attempt creation of the type at compile-time. The compiler would still have to emit the code to create the type at runtime as well, arbitrary types cannot be embedded as true constants in the CLR, IIRC.
@HaloFour I suspect if #119 or #8181 can do this, but you woudn't get compile-time error for that right? I don't want it to be embedded as constant (though constexpr
mentioned in #5474 can be another option), I want compile-time errors, for masochistic reasons.
@alrz
I assume that #119 would eventually lead to some kind of embedded metadata that would provide validation hints to the compiler for parameters but I doubt that would involve arbitrary code execution. I agree that compile-time enforcement of validation like that would be ideal but I envision that it would be based more on attributes decorating the parameter, e.g.
public static implicit operator VersionString([RegexMatches("...")] string str) {
return new VersionString(str);
}
@HaloFour Are you saying that we can only use conditions that have a built-in attribute for method contracts? And also, for that example, do I need to repeat all contracts for both operator and ctor? I think at least const
methods (#7626) can be evaluated at compile-time to not get into that mess. I don't think method contracts without compile-time errors are any different than if
.
@alrz
Considering that the notion of constexpr
doesn't exist in C# or the CLR I think the attribute route is more practical today. Even if #7626 were to be considered (with constexpr
) the rules would likely be quite strict (as they are with C++) and without support added throughout the BCL you probably couldn't make use of much in the way of supporting libraries, like regex. Even then I'd worry that it would be a pretty big potential attack vector. With C++ since it's compiling from source it can enforce pretty strict rules as to what the constexpr
function is allowed to do.
Either way, not particularly related to readonly parameters.
Still I think const
parameters can be useful, to ensure that the parameter came from a compile-time constant.
@stephentoub
The proposal doesn't cover readonly returns :neutral_face:
They make sense in the context of ref returns.
Like in this example:
C#
readonly ref header = buffer.Read<Header>();
It would be very useful for ReadOnlySpan<T>
whose indexer could return readonly ref
instead of a copy of the value.
update: Found it is mentioned in Proposal: Ref Returns and Locals
I don't see it mentioned in the proposal or comments, but would we allow for uninitialized readonly
locals where the compiler can ensure through flow analysis that at any attempted assignment to the local that at that point the local is definitely unassigned? This would be akin to Java's "blank final".
readonly int x;
if (condition) {
x = 1;
}
else {
x = CalculateValueForX();
}
We definitely need readonly locals/params if we want C# to be considered as practical language with functional programming concepts. Even Java gives you this possibility with final
keyword.
I would use existing readonly
keyword + shortcut options with let/val
like in initial proposal description.
Is it planned for C# 8?
It would be better to stick this feature with pattern/matching.
@aleks-sidorenko
This is obviously an important feature, but it doesn't stop you from writing functional code. Focus on referential transparency, avoid side effects that escape the function, be declarative, etc.
What does this really give us?
Besides piece of mind of course. Don't get me wrong, I like the idea from a self-documenting code perspective. But is there any performance or API design advantages?
-----Original Message-----
From: "Aluan Haddad" [email protected]
Sent: 6/28/2016 4:14 PM
To: "dotnet/roslyn" [email protected]
Cc: "Grauenwolf" [email protected]; "Manual" [email protected]
Subject: Re: [dotnet/roslyn] Proposal: 'readonly' for Locals and Parameters(#115)
@aleks-sidorenko
This is obviously an important feature, but it doesn't stop you from writing functional code. Focus on referential transparency, avoid side effects that escape the function, be declarative, etc.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or mute the thread.
@Grauenwolf no it's not about API design, parameters, with the exception of ref
and out
, are already read only from the perspective of the caller. It's about being able to write code that's more easy to reason about, more expressive of intent within the body of the method. I actually don't like the idea of read only parameters because it's something the caller has to look at but it only affects the callee. Read only locals on the other hand are a very good idea.
The proposal is not 100% clear on what it means for the calling code. I think it is quite important that ref should not be prefixed when calling the function. My understanding of having to prefix ref is to make it clear that the value might get mutated, which is an important thing to know. In the readonly ref case that is guaranteed not to happen, and it would actually make it unclear if the value is being mutated or not. I think it is also quite important to support operator overloads with readonly ref.
So I propose this:
Matrix3D myMatrix;
...
Use(myMatrix); // Note: ref is not necessary
public void Use(readonly ref Matrix3D matrix)
{
…
matrix.M31 = 0; // Error: can’t assign to readonly parameters
…
}
Some reasons for why I believe this is extremely important for C#'s future. At Unity we are very interested in seeing C# develop into a high performance language. We very much want to see this happen as soon as possible.
In Unity (using mono), ByValue takes 17.3ms while ByRef takes 3.5ms. In Unity and in game development in general the below is very real world common code that needs to be fast.
We are using a lot of structs because for games hitting 60 or 90FPS it is important that our customers can optimize their game to have zero GC allocations during a frame. This implies heavy usage of structs. And as you can see from the example below, having to prefix with ref looks quite ugly when doing math.
void Update()
{
Profiler.BeginSample("ByValue");
Matrix4x4 matrix = new Matrix4x4();
for (int i = 0;i<100000;i++)
{
var myValue = new Vector4();
var res = matrix * myValue;
}
Profiler.EndSample();
Profiler.BeginSample("ByRef");
for (int i = 0;i<100000;i++)
{
var myValue = new Vector4();
var res = Matrix4x4.mul(ref matrix, ref myValue);
}
Profiler.EndSample();
}
public struct Vector4
{
public float x;
public float y;
public float z;
public float w;
}
// A standard 4x4 transformation matrix.
public struct Matrix4x4
{
///*undocumented*
public float m00;
///*undocumented*
public float m10;
///*undocumented*
public float m20;
///*undocumented*
public float m30;
///*undocumented*
public float m01;
///*undocumented*
public float m11;
///*undocumented*
public float m21;
///*undocumented*
public float m31;
///*undocumented*
public float m02;
///*undocumented*
public float m12;
///*undocumented*
public float m22;
///*undocumented*
public float m32;
///*undocumented*
public float m03;
///*undocumented*
public float m13;
///*undocumented*
public float m23;
///*undocumented*
public float m33;
// Transforms a [[Vector4]] by a matrix.
static public Vector4 operator *(Matrix4x4 lhs, Vector4 v)
{
Vector4 res;
res.x = lhs.m00 * v.x + lhs.m01 * v.y + lhs.m02 * v.z + lhs.m03 * v.w;
res.y = lhs.m10 * v.x + lhs.m11 * v.y + lhs.m12 * v.z + lhs.m13 * v.w;
res.z = lhs.m20 * v.x + lhs.m21 * v.y + lhs.m22 * v.z + lhs.m23 * v.w;
res.w = lhs.m30 * v.x + lhs.m31 * v.y + lhs.m32 * v.z + lhs.m33 * v.w;
return res;
}
// Transforms a [[Vector4]] by a matrix.
static public Vector4 mul (ref Matrix4x4 lhs, ref Vector4 v)
{
Vector4 res;
res.x = lhs.m00 * v.x + lhs.m01 * v.y + lhs.m02 * v.z + lhs.m03 * v.w;
res.y = lhs.m10 * v.x + lhs.m11 * v.y + lhs.m12 * v.z + lhs.m13 * v.w;
res.z = lhs.m20 * v.x + lhs.m21 * v.y + lhs.m22 * v.z + lhs.m23 * v.w;
res.w = lhs.m30 * v.x + lhs.m31 * v.y + lhs.m32 * v.z + lhs.m33 * v.w;
return res;
}
}
@joeante ref keyword is not only for mark muted in method parameter, another usage of ref
keyword is pass by reference instead of pass by value that can help me to increase performance with preventing extra copy data for large struct
. So if I could not say readonly ref
, I could not say to user that API dose not want to change your data. and if I could not say to user, my API have not informative enough. and if I could not declare informative and accurate API with language, language features is poor.
I agree with @joeante that specifying readonly ref
at the call site should not be required. This does not seem to provide any useful information and makes what would likely be the _default_ use case for most performance-minded code significantly more wordy as well as making converting existing pass-by-value code to pass-by-readonly-ref a much more difficult task.
specifying
readonly ref
at the call site should not be required. This does not seem to provide any useful information
Whoa there. How are readonly ref
and ref
functionally the same? One is basically void *
the other is const void const *
. If C# is going to promote passing addresses around, it needs a way to specify that the value on the end of that address won't get tampered with.
Whoa there. How are readonly ref and ref functionally the same?
@whoisj I never said it was. I said "at the call site". Having to specify readonly ref
at the _call site_ would prevent overloaded operators that want to use readonly ref
for performance reasons from being possible, and it makes it considerably more 'wordy' to use on regular methods.
I would much rather see:
``` c#
var vectorC = vectorA + vectorB;
var dot = Vector.Dot( vectorA, vectorB );
than:
``` c#
var vectorC = Vector.Add( readonly ref vectorA, readonly ref vectorB );
var dot = Vector.Dot( readonly ref vectorA, readonly ref vectorB );
As was mentioned, most of the math/physics related code in a game engine would likely be written to use readonly ref
and it would be great if the code using it was as clean as possible.
@Ziflin You right, so I think we have agreed with readonly ref
at the call site does not necessary and should be avoid it, but readonly ref
in method declaration is necessary too to ensure that it will not change ( could not change) input parameter. this is necessary for define comprehensive API.
If any person have question about why a method writer declare input parameter ref
when does not need to change it? the answer is some of size of struct
( as you know struct
in C# pass by value in method ) is bigger than to pass by value with negligible performance cost, so method writer want to prevents extra cost and it is not possible except declare readonly ref
.
If asked again why big object declare as struct
and not declare as class
? the answer is some of struct
is outside of scope of method writer (sometime it is in another library) or for some design consideration writer does not like to change struct
to class
.
According to above reason I want to can declare such method as below:
C#
public void Add(readonly ref Vector vectorA, readonly ref Vector vectorB)
{
...
}
@joeante @Ziflin @soroshsabz shouldn’t pass by ref optimization just be something that the JITter does when it sees that the called method never tries to write to the struct and it’s readonly
in the caller? I don’t think such an optimization should require any new syntax (other than this proposal so that the caller can create and store the struct in a local readonly
instead of needing to instantiate a new class with a readonly
field).
@binki If JITter always does that work we would never request this feature. Because it never do its job as much as we want that why we need explicit syntax to force it to do
Not to mention making contract like virtual or interface
@Thaina I’m not sure what the current calling convention is, but a struct passed by value could safely be treated as COW by the synchronous parts of the callee. Such an optimization would work fine with interfaces too.
I’m just concerned that this issue is being hijacked by all these ideas about const correctness that are unrelated to it that will distract from the simple change of adding readonly
locals which behave like JavaScript’s const
. This simple feature will help the developer a lot with avoiding accidentally altering values that the developer intended to keep constant within the current scope.
Now, this does incidentally enable readonly ref
parameters, unless that ends up being explicitly disallowed. But the behavior of a readonly
struct field is very well defined and any existing C# programmer would expect a readonly ref
parameter to behave the same, so I don’t see the issue there either nor how this requires us to suddenly define const correctness just to get JavaScript const
-like readonly
locals (including parameters).
So can we keep the scope of this narrow? ;-)
This is now tracked at https://github.com/dotnet/csharplang/issues/188
I think readonly
is much better than let
. let
is meaningless in this context, and other than LINQ usage (I prefer methods-based LINQ anyway), it reminds of other, older languages. readonly
on the other hand is very to the point, you can read the code and it's obvious what it's intended to mean. Also, readonly
is already used for class fields with similar semantics. In fact, there is not a single reason to use let
instead of readonly
.
Instead of var a = 123;
, we can have readonly a = 123;
or readonly object o = "str";
@TahirAhmadov I agree with you that defining let
as equivalent to readonly var
is confusing. For older languages, I myself only konw of BASIC which had a LET
statement which actually mutated a variable (though it was not a declaration itself). For modern languages which those coming to C# would know, modern JavaScript uses let
for declaring a mutable variable. If let
is adopted, web developers with C# backends will have to keep straight that let
is assignable in one context and readonly
in the other.
I do have an issue with your suggestion of readonly a = 123;
. In the existing precedent of readonly
for class variables, it is always followed by a type. readonly object o = "str";
and readonly var s = "str";
make sense to me, but readonly o = "o";
both departs from the existing class variable syntax and would introduce ambiguity if explicitly specifying the variable’s type were allowed (which it should be for consistency with class variables).
However, a shorthand for readonly var
is necessary to get developer buy-in. People use var
because it is short—they would avoid using readonly var
just because it requires more typing and clutters the screen/code. Also, F# and LINQ are precedents of using let
to declare readonly
variables. Unless you can come up with a different keyword that is both short, easy to use, and also conveys the meaning more clearly than let
, I myself am going to continue hoping that let
is introduced as readonly var
.
Please see https://github.com/dotnet/csharplang/issues/188 and keep the discussion there. There has already been some discussion over let
there.
Most helpful comment
I'd like to allow the
readonly
modifier on classes to allow immutability by default, and methods also to correlate with C++const
functions.``` C#
readonly class Point
{
public int x; // Implicit readonly
public int y; // Implicit readonly
Point(int x, int y)
{
this.x = x;
this.y = y;
}
}
Point origin = new Point(0, 0);
origin.x = 1; // Error: can't reassign a readonly class member
```