C# has a ton of keywords, and it is sometimes hard to keep track of them all. One keyword that often gets lost in the shuffle is the readonly keyword, often because many people just group it with the const keyword and leave it at that. Well, the readonly keyword deserves better than that - and so here today we are going to talk about what exactly it does, how it differs from const, and why you would ever want to use it at all.
The readonly keyword does exactly what its name states - it makes a field read only. Well, I guess it doesn't quite mean just that - because there is one point in the lifespan of an object that a readonly field can be set. That point is during object initialization. And this fact right here is why readonly differs from const - a const field gets determined at compile time, while a readonly field is determined at runtime. For example:
{
public const string foo = "foo";
public MyClass()
{
foo = "bar";
}
}
//Error: The left-hand side of an assignment must be a variable, property or indexer
Here we have foo which is a constant. We can only set it in its declaration, anywhere else (including the constructor, as we see here) will result in a compile error. But with the readonly keyword, the above code compiles just fine:
{
public readonly string foo = "foo";
public MyClass()
{
foo = "bar";
}
}
This compiles because it is legal to set a readonly field at any point during the initialization of an object - both up in the field declaration and in the constructor. This means that we can actually dynamically determine the value of a readonly field at runtime by passing it in the constructor, or perhaps calculating it in the constructor:
{
public readonly string foo = "foo";
public MyClass(string abc)
{
foo = abc;
}
}
But if we try and set a readonly field at any other point, we get a compile error:
{
public readonly string foo = "foo";
public MyClass(string abc)
{
foo = abc;
}
public void SetFoo(string newFoo)
{
foo = newFoo;
}
}
//Error: A readonly field cannot be assigned to (except in a constructor or a variable initializer)
One somewhat surprising case at first (but not so surprising after you think about it a bit) where we cannot set a readonly has to do with inheritance. For example:
{
public readonly string foo = "foo";
public MyClass(string abc)
{
foo = abc;
}
}
public class MyExtClass : MyClass
{
public MyExtClass(string abc1, string abc2)
: base(abc1)
{ foo = abc2; }
}
//Error: A readonly field cannot be assigned to (except in a constructor or a variable initializer)
You cannot set a readonly belonging to a base class in the constructor for the current class. This makes sense after a moment of thought - the base class is already initialized (its constructor has already been run) and so letting a readonly field be modified here undermines what the readonly keyword is supposed to be accomplishing.
Ok, so now that we know what the readonly keyword is and what it does, why should we use it? There are a number of reasons, and they all have to do with code that is easier to read and think about. Sadly, there are no performance benefits to using the readonly keyword - none that I can find, anyway. This is somewhat perplexing - one would think there would be some performance benefits from using immutable variables- perhaps one of our readers will be able to tell me differently.
But anyway, back to the reasons you should use the readonly keyword. For instance, take the following code:
{
private string _foo = "foo";
public MyClass(string foo)
{
_foo = foo;
}
public string Foo
{
get { return _foo; }
}
}
There is nothing actually wrong with this code - we have a private string _foo and we expose it as a property Foo, only creating a getter. This essentially exposes a read only version of _foo. But if _foo isn't going to be changing internal to the class either, it is much nicer to write the code this way:
{
public readonly string foo = "foo";
public MyClass(string foo)
{
foo = foo;
}
}
Here, we lose the property altogether, and instead just have a public read only field.
Another reason to use the readonly keyword is that objects that are immutable are much easier to reason about. For example, here we have two objects, a three dimensional point object, and an element that needs to store its own position and notify others of changes:
{
public event EventHandler ValueChanged;
private float _x;
private float _y;
private float _z;
public Vector3(float x, float y, float z)
{
_x = x;
_y = y;
_z = z;
}
private void OnValueChanged()
{
if (ValueChanged != null)
ValueChanged(this, EventArgs.Empty);
}
public float X
{
get
{
return _x;
}
set
{
if (_x == value)
return;
_x = value;
OnValueChanged();
}
}
public float Y
{
get
{
return _y;
}
set
{
if (_y == value)
return;
_y = value;
OnValueChanged();
}
}
public float Z
{
get
{
return _z;
}
set
{
if (_z == value)
return;
_z = value;
OnValueChanged();
}
}
}
public class Element
{
public event EventHandler PositionChanged;
private Vector3 _position = null;
public Element(Vector3 pos)
{
Position = pos;
}
private void OnPositionChanged()
{
if (PositionChanged != null)
PositionChanged(this, EventArgs.Empty);
}
void Position_ValueChanged(object sender, EventArgs e)
{
OnPositionChanged();
}
public Vector3 Position
{
get { return _position; }
set
{
if (_position == value)
return;
if (_position != null)
_position.ValueChanged -= new EventHandler(Position_ValueChanged);
_position = value;
OnPositionChanged();
if (_position != null)
_position.ValueChanged += new EventHandler(Position_ValueChanged);
}
}
}
Thats a bunch of code! Mostly because we have to keep checking values and triggering events, so that we make sure that the final PositionChanged event always gets fired if anything about the position changes. Plus we have to worry about someone holding on to our Vector3 object for this element and passing it along, and passing it along, until it reaches the hands of someone who shouldn't be able to modify our position at all.
So lets take a look at how this code looks with the use of the readonly keyword, making the Vector3 object immutable:
{
public readonly float X;
public readonly float Y;
public readonly float Z;
public Vector3(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}
}
public class Element
{
public event EventHandler PositionChanged;
private Vector3 _position = null;
public Element(Vector3 pos)
{
Position = pos;
}
private void OnPositionChanged()
{
if (PositionChanged != null)
PositionChanged(this, EventArgs.Empty);
}
public Vector3 Position
{
get { return _position; }
set
{
if (_position == value)
return;
_position = value;
OnPositionChanged();
}
}
}
Wow, thats a lot less code! With this new code, we no longer need the event stuff all over the Vector3 object. Plus, we don't have to worry about who is holding on to the object that contains an element's position - because they won't actually be able to modify it. To give an element a new position now, you actually need to hand it a new Vector3 object.
Well, I hope you've enjoyed this discussion about the readonly keyword. And really, if any of you know anything about possible performance benefits of using the keyword (or have any other comments or questions), post a comment!
09/26/2007 - 13:21
Most blogs and literature on C# readonly key word emphasize how/why a readonly variable is unmodifiable outside the scope of construction and declaration.
I have scantly come across any literature that point out that if the readonly variable references a non-primitive object, the contents of the object are not "readonly" but modifiable outside of the scope of declaration/construction.
Use of readonly/final is when we need to prevent an object from being discarded and replaced with another object without inhibiting write access to contents of that object.
Therefore, c# non-primitive readonly objects are like java final non-primitive objects.
{
public int x, y;
}
public class Test
{
public readonly Register ChangeableContents =
new Register();
public void testChangeableContents()
{
//this is perfectly legal.
ChangeableContents.x = 10;
ChangeableContents.y = 100;
}
}
09/29/2007 - 17:21
But of course you still can't change the instance of that object. It works in the same way of properties without a setter. The way you mention about, you are not changing the "value" of that readonly field, just invoking some methods (or setters of properties, which are also some methods) of it.
So, you are right that it's possible to change the properties of a readonly object, but they are not the "contents" of that readonly field. The content is already set by "new Register()" in your sample, and the meaning of changing the content should allow you to re-new that object, which is not possible.
10/12/2007 - 04:25
In your example Vector would possibly be better as a struct.
10/12/2007 - 07:16
Yup, the Vector3 example would work better in some situations as a struct. But the readonly keyword is valid in a struct too, and you might still want to use it for one of the reasons mention above - to make it so that the only way a value in the vector can change is when the whole vector changes.
12/03/2008 - 14:12
Good Information Thanks for Sharing.
DotNetGuts
http://dotnetguts.blogspot.com
10/13/2009 - 22:18
I luv u.
03/28/2010 - 11:05
// cs_readonly_keyword.cs
// Readonly fields
using System;
public class ReadOnlyTest
{
class MyClass
{
public int x;
public readonly int y = 25; // Initialize a readonly field
public readonly int z;
public MyClass()
{
z = 24; // Initialize a readonly instance field
}
public MyClass(int p1, int p2, int p3)
{
x = p1;
y = p2;
z = p3;
}
}
public static void Main()
{
MyClass p1= new MyClass(11, 21, 32); // OK
Console.WriteLine("p1: x={0}, y={1}, z={2}" , p1.x, p1.y, p1.z);
MyClass p2 = new MyClass();
p2.x = 55; // OK
Console.WriteLine("p2: x={0}, y={1}, z={2}" , p2.x, p2.y, p2.z);
}
}
03/28/2010 - 11:07
Output
p1: x=11, y=21, z=32
p2: x=55, y=25, z=24
why the value of y changed in the first line output although it is defined readonly ???????????????????
03/29/2010 - 09:27
Because you set that in the constructor and it's allowed to change to value during initialization. You'd have to use const if it should not be editable in the constructor...
11/29/2011 - 10:34
Really, you should not expose as public any variables, even when they are marked readonly. This limits flexibility for any future changes to the API. At least with a property you have some flexibility to add additional code if necessary without affecting external classes.
The C# JIT is able to optimize away simple property accessors calls at runtime; so that eliminates the performance reason for not using the property.
I use readonly to make it clear to the reader that the object variable will not change during the life of the object and also to ensure future developers (or myself in the future) do not accidentally change that variable without carefully examining its usage.
Coming from Java which uses 'final' for this purpose and more, I really would like to see readonly usable on method parameters and local variables since this provides the same benefit (code clarity and compiler validation) locally.
Add Comment
[language] [/language]
Examples:
[javascript] [/javascript]
[actionscript] [/actionscript]
[csharp] [/csharp]
See here for supported languages.
Javascript must be enabled to submit anonymous comments - or you can login.