In c# readonly members can be reduced to readonly auto properties/expression-bodied members for immutable members is expression-bodied members are better then using readonly keywords?
Using readonly keywork:
public static readonly string COMPANY_NAME= "XYZ";
Using Expression-bodied Members:
public static string COMPANY_NAME => "XYZ";
I have come across various forums and solution that suggest expression bodied members for short hand use, and i could not find how it differs in performance.
Expression-bodied members provide a minimal and concise syntax to define properties and methods. It helps to eliminate boilerplate code and helps writing code that is more readable. The expression-bodied syntax can be used when a member's body consists only of one expression.
An expression-bodied method consists of a single expression that returns a value whose type matches the method's return type, or, for methods that return void , that performs some operation.
The Syntax of expression body definition is, member => expression; where expression should be a valid expression and member can be any from above list of type members.
Yes, you can.
Let's dive deep and see what compiler does for different types of fields.
class Program
{
public const string ConstString = "mesa const";
public static readonly string ReadonlyStatic = "mesa readonly";
public static string ExpressionBodied => "mesa expression";
public static string GetInitialized {get;} = "mesa get";
public static string GetWithBody { get { return "mesa get"; } }
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
System.Console.WriteLine("readonly:" + ReadonlyStatic);
System.Console.WriteLine("const:" + ConstString);
System.Console.WriteLine("expression bodied:" + ExpressionBodied);
System.Console.WriteLine("get initialized:" + GetInitialized);
System.Console.WriteLine("get with body:" + GetWithBody);
}
}
const string
creates a literal string
and will be evaluated at call site:
.field public static literal string ConstString = "mesa const"
// call site:
IL_0021: ldstr "const:mesa const"
IL_0026: call void [System.Console]System.Console::WriteLine(string)
static readonly
creates a field that is initialized in ctor and means only one field reference when used:
.field public static initonly string ReadonlyStatic
// call site:
IL_000c: ldstr "readonly:"
IL_0011: ldsfld string readonly_props.Program::ReadonlyStatic
IL_0016: call string [System.Runtime]System.String::Concat(string, string)
IL_001b: call void [System.Console]System.Console::WriteLine(string)
Expression bodied member generates a getter, which returns constant value:
.method public hidebysig static specialname string
get_ExpressionBodied() cil managed
{
.maxstack 8
// [9 50 - 9 67]
IL_0000: ldstr "mesa expression"
IL_0005: ret
} // end of method Program::get_ExpressionBodied
// call site:
IL_002c: ldstr "expression bodied:"
IL_0031: call string readonly_props.Program::get_ExpressionBodied()
IL_0036: call string [System.Runtime]System.String::Concat(string, string)
IL_003b: call void [System.Console]System.Console::WriteLine(string)
Readonly property with initialization generates an additional backing field for the initializing value.
.field private static initonly string '<GetInitialized>k__BackingField'
.method public hidebysig static specialname string
get_GetInitialized() cil managed
{
.custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
= (01 00 00 00 )
.maxstack 8
// [10 46 - 10 50]
IL_0000: ldsfld string readonly_props.Program::'<GetInitialized>k__BackingField'
IL_0005: ret
} // end of method Program::get_GetInitialized
// call site:
IL_0041: ldstr "get initialized:"
IL_0046: call string readonly_props.Program::get_GetInitialized()
IL_004b: call string [System.Runtime]System.String::Concat(string, string)
IL_0050: call void [System.Console]System.Console::WriteLine(string)
A Property getter with full body is a bit longer:
.method public hidebysig static specialname string
get_GetWithBody() cil managed
{
.maxstack 1
.locals init (
[0] string V_0
)
// [11 48 - 11 49]
IL_0000: nop
// [11 50 - 11 68]
IL_0001: ldstr "mesa get"
IL_0006: stloc.0 // V_0
IL_0007: br.s IL_0009
// [11 69 - 11 70]
IL_0009: ldloc.0 // V_0
IL_000a: ret
} // end of method Program::get_GetWithBody
// call site:
IL_0056: ldstr "get with body:"
IL_005b: call string readonly_props.Program::get_GetWithBody()
IL_0060: call string [System.Runtime]System.String::Concat(string, string)
IL_0065: call void [System.Console]System.Console::WriteLine(string)
From that, we can order them by amount of code (and calls) they generate:
const string
is definitely the fastest one, but can cause unexpected behavior in case of change, when used from other assembiles (as other answers mentioned)static readonly
comes right behind, with one field accessstatic string ExpressionBodied => "xxx"
will cause a method call (getter) that simply returns a constantstatic string GetInitialized {get;} = "xxx"
will result in a method call and a field access static string GetWithBody { get { return "xxx"; } }
will cause a method call that returns a constant, but wih additional memory allocation, it would seemIn practice, the performance differences will probably be unobservable. As pointed out, IL code can be further optimized by JIT, so you can end up with effectively the same performance. Nevertheless, I prefer to go with option 1. or 2.
First, you should use const
with string
constants, not readonly
. You should use the latter only for objects requiring a constructor call to construct them.
There is a side note to this though, as stated in comments, you should be aware that constants will be optimized even across assemblies (so your library constants can also be evaluated as constants on compile time by the referenced library). That would mean that with minor version updates, you could end up with another constant value in the assembly than in your library. In such cases you should keep using static readonly
.
Second, there is a huge difference between static readonly
fields and static
properties. The static
properties will get evaluated each and every time you call it. The static readonly
is slightly optimized, since it does only do a single assignment.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With