Does the 'readonly' modifier create a hidden copy of a field?Why calling Dispose() on BinaryReader results in compile error?What are the benefits to marking a field as `readonly` in C#?Is it ok to use the same interface definition but provide different behaviour?Why does Microsoft advise against readonly fields with mutable values?Passed value is not returnedHow to avoid “too many parameters” problem in API design?How to create readonly textbox in ASP.NET MVC3 RazorIf a folder does not exist, create itC# IDisposable correct usage and callHow to register and use different implementation of same interface?

The rigidity of the countable product of free groups

Why did Old English lose both thorn and eth?

Yet another hash table in C

The joke office

Efficiently defining a SparseArray function

What's it called when the bad guy gets eaten?

Why different specifications for telescopes and binoculars?

How to know if blackberries are safe to eat

Given a 32 bit number, what is an efficient way to scale each byte by a certain factor?

Through: how to use it with subtraction of functions?

What are the original Russian words for a prostitute?

Why does the Antonov AN-225 not have any winglets?

Why weren't bootable game disks ever a thing on the IBM PC?

What is the minimum time required for final wash in film development?

Would a carnivorous diet be able to support a giant worm?

When did "&" stop being taught alongside the alphabet?

What minifigure is this?

How can a dictatorship government be beneficial to a dictator in a post-scarcity society?

The three greedy pirates

How would the law enforce a ban on immortality?

Misrepresented my work history

What are some further readings in Econometrics you recommend?

Received a dinner invitation through my employer's email, is it ok to attend?

Write a function



Does the 'readonly' modifier create a hidden copy of a field?


Why calling Dispose() on BinaryReader results in compile error?What are the benefits to marking a field as `readonly` in C#?Is it ok to use the same interface definition but provide different behaviour?Why does Microsoft advise against readonly fields with mutable values?Passed value is not returnedHow to avoid “too many parameters” problem in API design?How to create readonly textbox in ASP.NET MVC3 RazorIf a folder does not exist, create itC# IDisposable correct usage and callHow to register and use different implementation of same interface?






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;








27















The only difference between MutableSlab and ImmutableSlab implementations is the readonly modifier applied on the handle field:



using System;
using System.Runtime.InteropServices;

public class Program

class MutableSlab : IDisposable

private GCHandle handle;

public MutableSlab()

this.handle = GCHandle.Alloc(new byte[256], GCHandleType.Pinned);


public bool IsAllocated => this.handle.IsAllocated;

public void Dispose()

this.handle.Free();



class ImmutableSlab : IDisposable

private readonly GCHandle handle;

public ImmutableSlab()

this.handle = GCHandle.Alloc(new byte[256], GCHandleType.Pinned);


public bool IsAllocated => this.handle.IsAllocated;

public void Dispose()

this.handle.Free();



public static void Main()

var mutableSlab = new MutableSlab();
var immutableSlab = new ImmutableSlab();

mutableSlab.Dispose();
immutableSlab.Dispose();

Console.WriteLine($"nameof(mutableSlab).handle.IsAllocated = mutableSlab.IsAllocated");
Console.WriteLine($"nameof(immutableSlab).handle.IsAllocated = immutableSlab.IsAllocated");




But they produce different results:



mutableSlab.handle.IsAllocated = False
immutableSlab.handle.IsAllocated = True


GCHandle is a mutable struct and when you copy it then it behaves exactly like in scenario with immutableSlab.



Does the readonly modifier create a hidden copy of a field? Does it mean that it's not only a compile-time check? I couldn't find anything about this behaviour here. Is this behaviour documented?










share|improve this question



















  • 1





    I won't post this as an answer since I'm not 100% sure about the behaviour of GC. But no, the readonly keyword doesn't introduce new fields. It does what it says on the tin. The behavior you observe is probably due to the GC not doing what you want it to. Try running GC.Collect(). The GC takes hints, not orders usually.

    – Markonius
    Jul 1 at 7:32






  • 2





    I'm writing an answer now... But for those who are impatient, here's a blog post I've written earlier: codeblog.jonskeet.uk/2014/07/16/…

    – Jon Skeet
    Jul 1 at 7:38







  • 1





    Member invocations via the read-only field creates a copy. It's not that there's an extra field - it's that the field is copied before invocation.

    – Jon Skeet
    Jul 1 at 7:40






  • 1





    Note that Resharper actually warns about this; for this.handle.Free(); in ImmutableSlab it gives the warning: "Impure method is called for readonly field of value type."

    – Matthew Watson
    Jul 1 at 7:42

















27















The only difference between MutableSlab and ImmutableSlab implementations is the readonly modifier applied on the handle field:



using System;
using System.Runtime.InteropServices;

public class Program

class MutableSlab : IDisposable

private GCHandle handle;

public MutableSlab()

this.handle = GCHandle.Alloc(new byte[256], GCHandleType.Pinned);


public bool IsAllocated => this.handle.IsAllocated;

public void Dispose()

this.handle.Free();



class ImmutableSlab : IDisposable

private readonly GCHandle handle;

public ImmutableSlab()

this.handle = GCHandle.Alloc(new byte[256], GCHandleType.Pinned);


public bool IsAllocated => this.handle.IsAllocated;

public void Dispose()

this.handle.Free();



public static void Main()

var mutableSlab = new MutableSlab();
var immutableSlab = new ImmutableSlab();

mutableSlab.Dispose();
immutableSlab.Dispose();

Console.WriteLine($"nameof(mutableSlab).handle.IsAllocated = mutableSlab.IsAllocated");
Console.WriteLine($"nameof(immutableSlab).handle.IsAllocated = immutableSlab.IsAllocated");




But they produce different results:



mutableSlab.handle.IsAllocated = False
immutableSlab.handle.IsAllocated = True


GCHandle is a mutable struct and when you copy it then it behaves exactly like in scenario with immutableSlab.



Does the readonly modifier create a hidden copy of a field? Does it mean that it's not only a compile-time check? I couldn't find anything about this behaviour here. Is this behaviour documented?










share|improve this question



















  • 1





    I won't post this as an answer since I'm not 100% sure about the behaviour of GC. But no, the readonly keyword doesn't introduce new fields. It does what it says on the tin. The behavior you observe is probably due to the GC not doing what you want it to. Try running GC.Collect(). The GC takes hints, not orders usually.

    – Markonius
    Jul 1 at 7:32






  • 2





    I'm writing an answer now... But for those who are impatient, here's a blog post I've written earlier: codeblog.jonskeet.uk/2014/07/16/…

    – Jon Skeet
    Jul 1 at 7:38







  • 1





    Member invocations via the read-only field creates a copy. It's not that there's an extra field - it's that the field is copied before invocation.

    – Jon Skeet
    Jul 1 at 7:40






  • 1





    Note that Resharper actually warns about this; for this.handle.Free(); in ImmutableSlab it gives the warning: "Impure method is called for readonly field of value type."

    – Matthew Watson
    Jul 1 at 7:42













27












27








27


3






The only difference between MutableSlab and ImmutableSlab implementations is the readonly modifier applied on the handle field:



using System;
using System.Runtime.InteropServices;

public class Program

class MutableSlab : IDisposable

private GCHandle handle;

public MutableSlab()

this.handle = GCHandle.Alloc(new byte[256], GCHandleType.Pinned);


public bool IsAllocated => this.handle.IsAllocated;

public void Dispose()

this.handle.Free();



class ImmutableSlab : IDisposable

private readonly GCHandle handle;

public ImmutableSlab()

this.handle = GCHandle.Alloc(new byte[256], GCHandleType.Pinned);


public bool IsAllocated => this.handle.IsAllocated;

public void Dispose()

this.handle.Free();



public static void Main()

var mutableSlab = new MutableSlab();
var immutableSlab = new ImmutableSlab();

mutableSlab.Dispose();
immutableSlab.Dispose();

Console.WriteLine($"nameof(mutableSlab).handle.IsAllocated = mutableSlab.IsAllocated");
Console.WriteLine($"nameof(immutableSlab).handle.IsAllocated = immutableSlab.IsAllocated");




But they produce different results:



mutableSlab.handle.IsAllocated = False
immutableSlab.handle.IsAllocated = True


GCHandle is a mutable struct and when you copy it then it behaves exactly like in scenario with immutableSlab.



Does the readonly modifier create a hidden copy of a field? Does it mean that it's not only a compile-time check? I couldn't find anything about this behaviour here. Is this behaviour documented?










share|improve this question
















The only difference between MutableSlab and ImmutableSlab implementations is the readonly modifier applied on the handle field:



using System;
using System.Runtime.InteropServices;

public class Program

class MutableSlab : IDisposable

private GCHandle handle;

public MutableSlab()

this.handle = GCHandle.Alloc(new byte[256], GCHandleType.Pinned);


public bool IsAllocated => this.handle.IsAllocated;

public void Dispose()

this.handle.Free();



class ImmutableSlab : IDisposable

private readonly GCHandle handle;

public ImmutableSlab()

this.handle = GCHandle.Alloc(new byte[256], GCHandleType.Pinned);


public bool IsAllocated => this.handle.IsAllocated;

public void Dispose()

this.handle.Free();



public static void Main()

var mutableSlab = new MutableSlab();
var immutableSlab = new ImmutableSlab();

mutableSlab.Dispose();
immutableSlab.Dispose();

Console.WriteLine($"nameof(mutableSlab).handle.IsAllocated = mutableSlab.IsAllocated");
Console.WriteLine($"nameof(immutableSlab).handle.IsAllocated = immutableSlab.IsAllocated");




But they produce different results:



mutableSlab.handle.IsAllocated = False
immutableSlab.handle.IsAllocated = True


GCHandle is a mutable struct and when you copy it then it behaves exactly like in scenario with immutableSlab.



Does the readonly modifier create a hidden copy of a field? Does it mean that it's not only a compile-time check? I couldn't find anything about this behaviour here. Is this behaviour documented?







c# struct value-type readonly-attribute






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jul 1 at 23:01









Peter Mortensen

14.2k19 gold badges88 silver badges115 bronze badges




14.2k19 gold badges88 silver badges115 bronze badges










asked Jul 1 at 7:26









BARTBART

5333 silver badges10 bronze badges




5333 silver badges10 bronze badges







  • 1





    I won't post this as an answer since I'm not 100% sure about the behaviour of GC. But no, the readonly keyword doesn't introduce new fields. It does what it says on the tin. The behavior you observe is probably due to the GC not doing what you want it to. Try running GC.Collect(). The GC takes hints, not orders usually.

    – Markonius
    Jul 1 at 7:32






  • 2





    I'm writing an answer now... But for those who are impatient, here's a blog post I've written earlier: codeblog.jonskeet.uk/2014/07/16/…

    – Jon Skeet
    Jul 1 at 7:38







  • 1





    Member invocations via the read-only field creates a copy. It's not that there's an extra field - it's that the field is copied before invocation.

    – Jon Skeet
    Jul 1 at 7:40






  • 1





    Note that Resharper actually warns about this; for this.handle.Free(); in ImmutableSlab it gives the warning: "Impure method is called for readonly field of value type."

    – Matthew Watson
    Jul 1 at 7:42












  • 1





    I won't post this as an answer since I'm not 100% sure about the behaviour of GC. But no, the readonly keyword doesn't introduce new fields. It does what it says on the tin. The behavior you observe is probably due to the GC not doing what you want it to. Try running GC.Collect(). The GC takes hints, not orders usually.

    – Markonius
    Jul 1 at 7:32






  • 2





    I'm writing an answer now... But for those who are impatient, here's a blog post I've written earlier: codeblog.jonskeet.uk/2014/07/16/…

    – Jon Skeet
    Jul 1 at 7:38







  • 1





    Member invocations via the read-only field creates a copy. It's not that there's an extra field - it's that the field is copied before invocation.

    – Jon Skeet
    Jul 1 at 7:40






  • 1





    Note that Resharper actually warns about this; for this.handle.Free(); in ImmutableSlab it gives the warning: "Impure method is called for readonly field of value type."

    – Matthew Watson
    Jul 1 at 7:42







1




1





I won't post this as an answer since I'm not 100% sure about the behaviour of GC. But no, the readonly keyword doesn't introduce new fields. It does what it says on the tin. The behavior you observe is probably due to the GC not doing what you want it to. Try running GC.Collect(). The GC takes hints, not orders usually.

– Markonius
Jul 1 at 7:32





I won't post this as an answer since I'm not 100% sure about the behaviour of GC. But no, the readonly keyword doesn't introduce new fields. It does what it says on the tin. The behavior you observe is probably due to the GC not doing what you want it to. Try running GC.Collect(). The GC takes hints, not orders usually.

– Markonius
Jul 1 at 7:32




2




2





I'm writing an answer now... But for those who are impatient, here's a blog post I've written earlier: codeblog.jonskeet.uk/2014/07/16/…

– Jon Skeet
Jul 1 at 7:38






I'm writing an answer now... But for those who are impatient, here's a blog post I've written earlier: codeblog.jonskeet.uk/2014/07/16/…

– Jon Skeet
Jul 1 at 7:38





1




1





Member invocations via the read-only field creates a copy. It's not that there's an extra field - it's that the field is copied before invocation.

– Jon Skeet
Jul 1 at 7:40





Member invocations via the read-only field creates a copy. It's not that there's an extra field - it's that the field is copied before invocation.

– Jon Skeet
Jul 1 at 7:40




1




1





Note that Resharper actually warns about this; for this.handle.Free(); in ImmutableSlab it gives the warning: "Impure method is called for readonly field of value type."

– Matthew Watson
Jul 1 at 7:42





Note that Resharper actually warns about this; for this.handle.Free(); in ImmutableSlab it gives the warning: "Impure method is called for readonly field of value type."

– Matthew Watson
Jul 1 at 7:42












1 Answer
1






active

oldest

votes


















28















Does the readonly modifier create a hidden copy of a field?




Calling a method or property on a read-only field of a regular struct type (outside the constructor or static constructor) first copies the field, yes. That's because the compiler doesn't know whether the property or method access would modify the value you call it on.



From the C# 5 ECMA specification:



Section 12.7.5.1 (Member access, general)



This classifies member accesses, including:




  • If I identifies a static field:

    • If the field is readonly and the reference occurs outside the static constructor of the class or struct in which the field is declared, then the result is a value, namely the value of the static field I in E.

    • Otherwise, the result is a variable, namely the static field I in E.




And:




  • If T is a struct-type and I identifies an instance field of that struct-type:

    • If E is a value, or if the field is readonly and the reference occurs outside an instance constructor of the struct in which the field is declared, then the result is a value, namely the value of the field I in the struct instance given by E.

    • Otherwise, the result is a variable, namely the field I in the struct instance given by E.




I'm not sure why the instance field part specifically refers to struct types, but the static field part doesn't. The important part is whether the expression is classified as a variable or a value. That's then important in function member invocation...



Section 12.6.6.1 (Function member invocation, general)




The run-time processing of a function member invocation consists of the following steps, where M is the function member and, if M is an instance member, E is the instance expression:



[...]



  • Otherwise, if the type of E is a value-type V, and M is declared or overridden in V:

    • [...]

    • If E is not classified as a variable, then a temporary local variable of E's type is created and the value of E is assigned to that variable. E is then reclassified as a reference to that temporary local variable. The temporary variable is accessible as this within M, but not in any other way. Thus, only when E is a true variable is it possible for the caller to observe the changes that M makes to this.




Here's a self-contained example:



using System;
using System.Globalization;

struct Counter

private int count;

public int IncrementedCount => ++count;


class Test

static readonly Counter readOnlyCounter;
static Counter readWriteCounter;

static void Main()

Console.WriteLine(readOnlyCounter.IncrementedCount); // 1
Console.WriteLine(readOnlyCounter.IncrementedCount); // 1
Console.WriteLine(readOnlyCounter.IncrementedCount); // 1

Console.WriteLine(readWriteCounter.IncrementedCount); // 1
Console.WriteLine(readWriteCounter.IncrementedCount); // 2
Console.WriteLine(readWriteCounter.IncrementedCount); // 3




Here's the IL for a call to readOnlyCounter.IncrementedCount:



ldsfld valuetype Counter Test::readOnlyCounter
stloc.0
ldloca.s V_0
call instance int32 Counter::get_IncrementedCount()


That copies the field value onto the stack, then calls the property... so the value of the field doesn't end up changing; it's incrementing count within the copy.



Compare that with the IL for the read-write field:



ldsflda valuetype Counter Test::readWriteCounter
call instance int32 Counter::get_IncrementedCount()


That makes the call directly on the field, so the field value ends up changing within the property.



Making a copy can be inefficient when the struct is large and the member doesn't mutate it. That's why in C# 7.2 and above, the readonly modifier can be applied to a struct. Here's another example:



using System;
using System.Globalization;

readonly struct ReadOnlyStruct

public void NoOp()


class Test

static readonly ReadOnlyStruct field1;
static ReadOnlyStruct field2;

static void Main()

field1.NoOp();
field2.NoOp();




With the readonly modifier on the struct itself, the field1.NoOp() call doesn't create a copy. If you remove the readonly modifier and recompile, you'll see that it creates a copy just like it did in readOnlyCounter.IncrementedCount.



I have a blog post from 2014 that I wrote having found that readonly fields were causing performance issues in Noda Time. Fortunately that's now fixed using the readonly modifier on the structs instead.






share|improve this answer

























  • Calling a method or property on a read-only field of a regular struct type first copies the field. I couldn't find this statement in documentation, but I think this is the implicit version of it: Because value types directly contain their data, a field that is a readonly value type is immutable. So if the field is not a readonly struct then when I call impure method that changes the state of this field then it must create a copy. Am I right?

    – BART
    Jul 1 at 8:12












  • @BART: Yes. Which documentation were you looking at? It is in the C# specification somewhere, but it may not be terribly easy to find.

    – Jon Skeet
    Jul 1 at 8:27











  • @Jon Skeet I was looking at the microsoft site readonly keyword, but there is no such explicit statement like yours. I will take a deeper look at C# specification. Thanks for the explanation

    – BART
    Jul 1 at 8:39











  • @BART: I've now quoted the relevant bit of the spec, and I'll look at the confusing part of the text.

    – Jon Skeet
    Jul 1 at 8:39












  • Thanks for the comprehensive explanation, Jon! Before that I thought that readonly was only referring to the reference variable, not to the object itself. I learned something new! Thumbs up for that answer :-)

    – Matt
    Jul 1 at 9:21










Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













draft saved

draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f56831532%2fdoes-the-readonly-modifier-create-a-hidden-copy-of-a-field%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









28















Does the readonly modifier create a hidden copy of a field?




Calling a method or property on a read-only field of a regular struct type (outside the constructor or static constructor) first copies the field, yes. That's because the compiler doesn't know whether the property or method access would modify the value you call it on.



From the C# 5 ECMA specification:



Section 12.7.5.1 (Member access, general)



This classifies member accesses, including:




  • If I identifies a static field:

    • If the field is readonly and the reference occurs outside the static constructor of the class or struct in which the field is declared, then the result is a value, namely the value of the static field I in E.

    • Otherwise, the result is a variable, namely the static field I in E.




And:




  • If T is a struct-type and I identifies an instance field of that struct-type:

    • If E is a value, or if the field is readonly and the reference occurs outside an instance constructor of the struct in which the field is declared, then the result is a value, namely the value of the field I in the struct instance given by E.

    • Otherwise, the result is a variable, namely the field I in the struct instance given by E.




I'm not sure why the instance field part specifically refers to struct types, but the static field part doesn't. The important part is whether the expression is classified as a variable or a value. That's then important in function member invocation...



Section 12.6.6.1 (Function member invocation, general)




The run-time processing of a function member invocation consists of the following steps, where M is the function member and, if M is an instance member, E is the instance expression:



[...]



  • Otherwise, if the type of E is a value-type V, and M is declared or overridden in V:

    • [...]

    • If E is not classified as a variable, then a temporary local variable of E's type is created and the value of E is assigned to that variable. E is then reclassified as a reference to that temporary local variable. The temporary variable is accessible as this within M, but not in any other way. Thus, only when E is a true variable is it possible for the caller to observe the changes that M makes to this.




Here's a self-contained example:



using System;
using System.Globalization;

struct Counter

private int count;

public int IncrementedCount => ++count;


class Test

static readonly Counter readOnlyCounter;
static Counter readWriteCounter;

static void Main()

Console.WriteLine(readOnlyCounter.IncrementedCount); // 1
Console.WriteLine(readOnlyCounter.IncrementedCount); // 1
Console.WriteLine(readOnlyCounter.IncrementedCount); // 1

Console.WriteLine(readWriteCounter.IncrementedCount); // 1
Console.WriteLine(readWriteCounter.IncrementedCount); // 2
Console.WriteLine(readWriteCounter.IncrementedCount); // 3




Here's the IL for a call to readOnlyCounter.IncrementedCount:



ldsfld valuetype Counter Test::readOnlyCounter
stloc.0
ldloca.s V_0
call instance int32 Counter::get_IncrementedCount()


That copies the field value onto the stack, then calls the property... so the value of the field doesn't end up changing; it's incrementing count within the copy.



Compare that with the IL for the read-write field:



ldsflda valuetype Counter Test::readWriteCounter
call instance int32 Counter::get_IncrementedCount()


That makes the call directly on the field, so the field value ends up changing within the property.



Making a copy can be inefficient when the struct is large and the member doesn't mutate it. That's why in C# 7.2 and above, the readonly modifier can be applied to a struct. Here's another example:



using System;
using System.Globalization;

readonly struct ReadOnlyStruct

public void NoOp()


class Test

static readonly ReadOnlyStruct field1;
static ReadOnlyStruct field2;

static void Main()

field1.NoOp();
field2.NoOp();




With the readonly modifier on the struct itself, the field1.NoOp() call doesn't create a copy. If you remove the readonly modifier and recompile, you'll see that it creates a copy just like it did in readOnlyCounter.IncrementedCount.



I have a blog post from 2014 that I wrote having found that readonly fields were causing performance issues in Noda Time. Fortunately that's now fixed using the readonly modifier on the structs instead.






share|improve this answer

























  • Calling a method or property on a read-only field of a regular struct type first copies the field. I couldn't find this statement in documentation, but I think this is the implicit version of it: Because value types directly contain their data, a field that is a readonly value type is immutable. So if the field is not a readonly struct then when I call impure method that changes the state of this field then it must create a copy. Am I right?

    – BART
    Jul 1 at 8:12












  • @BART: Yes. Which documentation were you looking at? It is in the C# specification somewhere, but it may not be terribly easy to find.

    – Jon Skeet
    Jul 1 at 8:27











  • @Jon Skeet I was looking at the microsoft site readonly keyword, but there is no such explicit statement like yours. I will take a deeper look at C# specification. Thanks for the explanation

    – BART
    Jul 1 at 8:39











  • @BART: I've now quoted the relevant bit of the spec, and I'll look at the confusing part of the text.

    – Jon Skeet
    Jul 1 at 8:39












  • Thanks for the comprehensive explanation, Jon! Before that I thought that readonly was only referring to the reference variable, not to the object itself. I learned something new! Thumbs up for that answer :-)

    – Matt
    Jul 1 at 9:21















28















Does the readonly modifier create a hidden copy of a field?




Calling a method or property on a read-only field of a regular struct type (outside the constructor or static constructor) first copies the field, yes. That's because the compiler doesn't know whether the property or method access would modify the value you call it on.



From the C# 5 ECMA specification:



Section 12.7.5.1 (Member access, general)



This classifies member accesses, including:




  • If I identifies a static field:

    • If the field is readonly and the reference occurs outside the static constructor of the class or struct in which the field is declared, then the result is a value, namely the value of the static field I in E.

    • Otherwise, the result is a variable, namely the static field I in E.




And:




  • If T is a struct-type and I identifies an instance field of that struct-type:

    • If E is a value, or if the field is readonly and the reference occurs outside an instance constructor of the struct in which the field is declared, then the result is a value, namely the value of the field I in the struct instance given by E.

    • Otherwise, the result is a variable, namely the field I in the struct instance given by E.




I'm not sure why the instance field part specifically refers to struct types, but the static field part doesn't. The important part is whether the expression is classified as a variable or a value. That's then important in function member invocation...



Section 12.6.6.1 (Function member invocation, general)




The run-time processing of a function member invocation consists of the following steps, where M is the function member and, if M is an instance member, E is the instance expression:



[...]



  • Otherwise, if the type of E is a value-type V, and M is declared or overridden in V:

    • [...]

    • If E is not classified as a variable, then a temporary local variable of E's type is created and the value of E is assigned to that variable. E is then reclassified as a reference to that temporary local variable. The temporary variable is accessible as this within M, but not in any other way. Thus, only when E is a true variable is it possible for the caller to observe the changes that M makes to this.




Here's a self-contained example:



using System;
using System.Globalization;

struct Counter

private int count;

public int IncrementedCount => ++count;


class Test

static readonly Counter readOnlyCounter;
static Counter readWriteCounter;

static void Main()

Console.WriteLine(readOnlyCounter.IncrementedCount); // 1
Console.WriteLine(readOnlyCounter.IncrementedCount); // 1
Console.WriteLine(readOnlyCounter.IncrementedCount); // 1

Console.WriteLine(readWriteCounter.IncrementedCount); // 1
Console.WriteLine(readWriteCounter.IncrementedCount); // 2
Console.WriteLine(readWriteCounter.IncrementedCount); // 3




Here's the IL for a call to readOnlyCounter.IncrementedCount:



ldsfld valuetype Counter Test::readOnlyCounter
stloc.0
ldloca.s V_0
call instance int32 Counter::get_IncrementedCount()


That copies the field value onto the stack, then calls the property... so the value of the field doesn't end up changing; it's incrementing count within the copy.



Compare that with the IL for the read-write field:



ldsflda valuetype Counter Test::readWriteCounter
call instance int32 Counter::get_IncrementedCount()


That makes the call directly on the field, so the field value ends up changing within the property.



Making a copy can be inefficient when the struct is large and the member doesn't mutate it. That's why in C# 7.2 and above, the readonly modifier can be applied to a struct. Here's another example:



using System;
using System.Globalization;

readonly struct ReadOnlyStruct

public void NoOp()


class Test

static readonly ReadOnlyStruct field1;
static ReadOnlyStruct field2;

static void Main()

field1.NoOp();
field2.NoOp();




With the readonly modifier on the struct itself, the field1.NoOp() call doesn't create a copy. If you remove the readonly modifier and recompile, you'll see that it creates a copy just like it did in readOnlyCounter.IncrementedCount.



I have a blog post from 2014 that I wrote having found that readonly fields were causing performance issues in Noda Time. Fortunately that's now fixed using the readonly modifier on the structs instead.






share|improve this answer

























  • Calling a method or property on a read-only field of a regular struct type first copies the field. I couldn't find this statement in documentation, but I think this is the implicit version of it: Because value types directly contain their data, a field that is a readonly value type is immutable. So if the field is not a readonly struct then when I call impure method that changes the state of this field then it must create a copy. Am I right?

    – BART
    Jul 1 at 8:12












  • @BART: Yes. Which documentation were you looking at? It is in the C# specification somewhere, but it may not be terribly easy to find.

    – Jon Skeet
    Jul 1 at 8:27











  • @Jon Skeet I was looking at the microsoft site readonly keyword, but there is no such explicit statement like yours. I will take a deeper look at C# specification. Thanks for the explanation

    – BART
    Jul 1 at 8:39











  • @BART: I've now quoted the relevant bit of the spec, and I'll look at the confusing part of the text.

    – Jon Skeet
    Jul 1 at 8:39












  • Thanks for the comprehensive explanation, Jon! Before that I thought that readonly was only referring to the reference variable, not to the object itself. I learned something new! Thumbs up for that answer :-)

    – Matt
    Jul 1 at 9:21













28












28








28








Does the readonly modifier create a hidden copy of a field?




Calling a method or property on a read-only field of a regular struct type (outside the constructor or static constructor) first copies the field, yes. That's because the compiler doesn't know whether the property or method access would modify the value you call it on.



From the C# 5 ECMA specification:



Section 12.7.5.1 (Member access, general)



This classifies member accesses, including:




  • If I identifies a static field:

    • If the field is readonly and the reference occurs outside the static constructor of the class or struct in which the field is declared, then the result is a value, namely the value of the static field I in E.

    • Otherwise, the result is a variable, namely the static field I in E.




And:




  • If T is a struct-type and I identifies an instance field of that struct-type:

    • If E is a value, or if the field is readonly and the reference occurs outside an instance constructor of the struct in which the field is declared, then the result is a value, namely the value of the field I in the struct instance given by E.

    • Otherwise, the result is a variable, namely the field I in the struct instance given by E.




I'm not sure why the instance field part specifically refers to struct types, but the static field part doesn't. The important part is whether the expression is classified as a variable or a value. That's then important in function member invocation...



Section 12.6.6.1 (Function member invocation, general)




The run-time processing of a function member invocation consists of the following steps, where M is the function member and, if M is an instance member, E is the instance expression:



[...]



  • Otherwise, if the type of E is a value-type V, and M is declared or overridden in V:

    • [...]

    • If E is not classified as a variable, then a temporary local variable of E's type is created and the value of E is assigned to that variable. E is then reclassified as a reference to that temporary local variable. The temporary variable is accessible as this within M, but not in any other way. Thus, only when E is a true variable is it possible for the caller to observe the changes that M makes to this.




Here's a self-contained example:



using System;
using System.Globalization;

struct Counter

private int count;

public int IncrementedCount => ++count;


class Test

static readonly Counter readOnlyCounter;
static Counter readWriteCounter;

static void Main()

Console.WriteLine(readOnlyCounter.IncrementedCount); // 1
Console.WriteLine(readOnlyCounter.IncrementedCount); // 1
Console.WriteLine(readOnlyCounter.IncrementedCount); // 1

Console.WriteLine(readWriteCounter.IncrementedCount); // 1
Console.WriteLine(readWriteCounter.IncrementedCount); // 2
Console.WriteLine(readWriteCounter.IncrementedCount); // 3




Here's the IL for a call to readOnlyCounter.IncrementedCount:



ldsfld valuetype Counter Test::readOnlyCounter
stloc.0
ldloca.s V_0
call instance int32 Counter::get_IncrementedCount()


That copies the field value onto the stack, then calls the property... so the value of the field doesn't end up changing; it's incrementing count within the copy.



Compare that with the IL for the read-write field:



ldsflda valuetype Counter Test::readWriteCounter
call instance int32 Counter::get_IncrementedCount()


That makes the call directly on the field, so the field value ends up changing within the property.



Making a copy can be inefficient when the struct is large and the member doesn't mutate it. That's why in C# 7.2 and above, the readonly modifier can be applied to a struct. Here's another example:



using System;
using System.Globalization;

readonly struct ReadOnlyStruct

public void NoOp()


class Test

static readonly ReadOnlyStruct field1;
static ReadOnlyStruct field2;

static void Main()

field1.NoOp();
field2.NoOp();




With the readonly modifier on the struct itself, the field1.NoOp() call doesn't create a copy. If you remove the readonly modifier and recompile, you'll see that it creates a copy just like it did in readOnlyCounter.IncrementedCount.



I have a blog post from 2014 that I wrote having found that readonly fields were causing performance issues in Noda Time. Fortunately that's now fixed using the readonly modifier on the structs instead.






share|improve this answer
















Does the readonly modifier create a hidden copy of a field?




Calling a method or property on a read-only field of a regular struct type (outside the constructor or static constructor) first copies the field, yes. That's because the compiler doesn't know whether the property or method access would modify the value you call it on.



From the C# 5 ECMA specification:



Section 12.7.5.1 (Member access, general)



This classifies member accesses, including:




  • If I identifies a static field:

    • If the field is readonly and the reference occurs outside the static constructor of the class or struct in which the field is declared, then the result is a value, namely the value of the static field I in E.

    • Otherwise, the result is a variable, namely the static field I in E.




And:




  • If T is a struct-type and I identifies an instance field of that struct-type:

    • If E is a value, or if the field is readonly and the reference occurs outside an instance constructor of the struct in which the field is declared, then the result is a value, namely the value of the field I in the struct instance given by E.

    • Otherwise, the result is a variable, namely the field I in the struct instance given by E.




I'm not sure why the instance field part specifically refers to struct types, but the static field part doesn't. The important part is whether the expression is classified as a variable or a value. That's then important in function member invocation...



Section 12.6.6.1 (Function member invocation, general)




The run-time processing of a function member invocation consists of the following steps, where M is the function member and, if M is an instance member, E is the instance expression:



[...]



  • Otherwise, if the type of E is a value-type V, and M is declared or overridden in V:

    • [...]

    • If E is not classified as a variable, then a temporary local variable of E's type is created and the value of E is assigned to that variable. E is then reclassified as a reference to that temporary local variable. The temporary variable is accessible as this within M, but not in any other way. Thus, only when E is a true variable is it possible for the caller to observe the changes that M makes to this.




Here's a self-contained example:



using System;
using System.Globalization;

struct Counter

private int count;

public int IncrementedCount => ++count;


class Test

static readonly Counter readOnlyCounter;
static Counter readWriteCounter;

static void Main()

Console.WriteLine(readOnlyCounter.IncrementedCount); // 1
Console.WriteLine(readOnlyCounter.IncrementedCount); // 1
Console.WriteLine(readOnlyCounter.IncrementedCount); // 1

Console.WriteLine(readWriteCounter.IncrementedCount); // 1
Console.WriteLine(readWriteCounter.IncrementedCount); // 2
Console.WriteLine(readWriteCounter.IncrementedCount); // 3




Here's the IL for a call to readOnlyCounter.IncrementedCount:



ldsfld valuetype Counter Test::readOnlyCounter
stloc.0
ldloca.s V_0
call instance int32 Counter::get_IncrementedCount()


That copies the field value onto the stack, then calls the property... so the value of the field doesn't end up changing; it's incrementing count within the copy.



Compare that with the IL for the read-write field:



ldsflda valuetype Counter Test::readWriteCounter
call instance int32 Counter::get_IncrementedCount()


That makes the call directly on the field, so the field value ends up changing within the property.



Making a copy can be inefficient when the struct is large and the member doesn't mutate it. That's why in C# 7.2 and above, the readonly modifier can be applied to a struct. Here's another example:



using System;
using System.Globalization;

readonly struct ReadOnlyStruct

public void NoOp()


class Test

static readonly ReadOnlyStruct field1;
static ReadOnlyStruct field2;

static void Main()

field1.NoOp();
field2.NoOp();




With the readonly modifier on the struct itself, the field1.NoOp() call doesn't create a copy. If you remove the readonly modifier and recompile, you'll see that it creates a copy just like it did in readOnlyCounter.IncrementedCount.



I have a blog post from 2014 that I wrote having found that readonly fields were causing performance issues in Noda Time. Fortunately that's now fixed using the readonly modifier on the structs instead.







share|improve this answer














share|improve this answer



share|improve this answer








edited Jul 1 at 23:01









Peter Mortensen

14.2k19 gold badges88 silver badges115 bronze badges




14.2k19 gold badges88 silver badges115 bronze badges










answered Jul 1 at 7:46









Jon SkeetJon Skeet

1117k709 gold badges8134 silver badges8540 bronze badges




1117k709 gold badges8134 silver badges8540 bronze badges












  • Calling a method or property on a read-only field of a regular struct type first copies the field. I couldn't find this statement in documentation, but I think this is the implicit version of it: Because value types directly contain their data, a field that is a readonly value type is immutable. So if the field is not a readonly struct then when I call impure method that changes the state of this field then it must create a copy. Am I right?

    – BART
    Jul 1 at 8:12












  • @BART: Yes. Which documentation were you looking at? It is in the C# specification somewhere, but it may not be terribly easy to find.

    – Jon Skeet
    Jul 1 at 8:27











  • @Jon Skeet I was looking at the microsoft site readonly keyword, but there is no such explicit statement like yours. I will take a deeper look at C# specification. Thanks for the explanation

    – BART
    Jul 1 at 8:39











  • @BART: I've now quoted the relevant bit of the spec, and I'll look at the confusing part of the text.

    – Jon Skeet
    Jul 1 at 8:39












  • Thanks for the comprehensive explanation, Jon! Before that I thought that readonly was only referring to the reference variable, not to the object itself. I learned something new! Thumbs up for that answer :-)

    – Matt
    Jul 1 at 9:21

















  • Calling a method or property on a read-only field of a regular struct type first copies the field. I couldn't find this statement in documentation, but I think this is the implicit version of it: Because value types directly contain their data, a field that is a readonly value type is immutable. So if the field is not a readonly struct then when I call impure method that changes the state of this field then it must create a copy. Am I right?

    – BART
    Jul 1 at 8:12












  • @BART: Yes. Which documentation were you looking at? It is in the C# specification somewhere, but it may not be terribly easy to find.

    – Jon Skeet
    Jul 1 at 8:27











  • @Jon Skeet I was looking at the microsoft site readonly keyword, but there is no such explicit statement like yours. I will take a deeper look at C# specification. Thanks for the explanation

    – BART
    Jul 1 at 8:39











  • @BART: I've now quoted the relevant bit of the spec, and I'll look at the confusing part of the text.

    – Jon Skeet
    Jul 1 at 8:39












  • Thanks for the comprehensive explanation, Jon! Before that I thought that readonly was only referring to the reference variable, not to the object itself. I learned something new! Thumbs up for that answer :-)

    – Matt
    Jul 1 at 9:21
















Calling a method or property on a read-only field of a regular struct type first copies the field. I couldn't find this statement in documentation, but I think this is the implicit version of it: Because value types directly contain their data, a field that is a readonly value type is immutable. So if the field is not a readonly struct then when I call impure method that changes the state of this field then it must create a copy. Am I right?

– BART
Jul 1 at 8:12






Calling a method or property on a read-only field of a regular struct type first copies the field. I couldn't find this statement in documentation, but I think this is the implicit version of it: Because value types directly contain their data, a field that is a readonly value type is immutable. So if the field is not a readonly struct then when I call impure method that changes the state of this field then it must create a copy. Am I right?

– BART
Jul 1 at 8:12














@BART: Yes. Which documentation were you looking at? It is in the C# specification somewhere, but it may not be terribly easy to find.

– Jon Skeet
Jul 1 at 8:27





@BART: Yes. Which documentation were you looking at? It is in the C# specification somewhere, but it may not be terribly easy to find.

– Jon Skeet
Jul 1 at 8:27













@Jon Skeet I was looking at the microsoft site readonly keyword, but there is no such explicit statement like yours. I will take a deeper look at C# specification. Thanks for the explanation

– BART
Jul 1 at 8:39





@Jon Skeet I was looking at the microsoft site readonly keyword, but there is no such explicit statement like yours. I will take a deeper look at C# specification. Thanks for the explanation

– BART
Jul 1 at 8:39













@BART: I've now quoted the relevant bit of the spec, and I'll look at the confusing part of the text.

– Jon Skeet
Jul 1 at 8:39






@BART: I've now quoted the relevant bit of the spec, and I'll look at the confusing part of the text.

– Jon Skeet
Jul 1 at 8:39














Thanks for the comprehensive explanation, Jon! Before that I thought that readonly was only referring to the reference variable, not to the object itself. I learned something new! Thumbs up for that answer :-)

– Matt
Jul 1 at 9:21





Thanks for the comprehensive explanation, Jon! Before that I thought that readonly was only referring to the reference variable, not to the object itself. I learned something new! Thumbs up for that answer :-)

– Matt
Jul 1 at 9:21








Got a question that you can’t ask on public Stack Overflow? Learn more about sharing private information with Stack Overflow for Teams.







Got a question that you can’t ask on public Stack Overflow? Learn more about sharing private information with Stack Overflow for Teams.



















draft saved

draft discarded
















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f56831532%2fdoes-the-readonly-modifier-create-a-hidden-copy-of-a-field%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Get product attribute by attribute group code in magento 2get product attribute by product attribute group in magento 2Magento 2 Log Bundle Product Data in List Page?How to get all product attribute of a attribute group of Default attribute set?Magento 2.1 Create a filter in the product grid by new attributeMagento 2 : Get Product Attribute values By GroupMagento 2 How to get all existing values for one attributeMagento 2 get custom attribute of a single product inside a pluginMagento 2.3 How to get all the Multi Source Inventory (MSI) locations collection in custom module?Magento2: how to develop rest API to get new productsGet product attribute by attribute group code ( [attribute_group_code] ) in magento 2

Category:9 (number) SubcategoriesMedia in category "9 (number)"Navigation menuUpload mediaGND ID: 4485639-8Library of Congress authority ID: sh85091979ReasonatorScholiaStatistics

Magento 2.3: How do i solve this, Not registered handle, on custom form?How can i rewrite TierPrice Block in Magento2magento 2 captcha not rendering if I override layout xmlmain.CRITICAL: Plugin class doesn't existMagento 2 : Problem while adding custom button order view page?Magento 2.2.5: Overriding Admin Controller sales/orderMagento 2.2.5: Add, Update and Delete existing products Custom OptionsMagento 2.3 : File Upload issue in UI Component FormMagento2 Not registered handleHow to configured Form Builder Js in my custom magento 2.3.0 module?Magento 2.3. How to create image upload field in an admin form