I can always find legitimate discussions about C# features, and there are almost always pros and cons to everything, but in this case, I really couldn't find anyone who recommended against using auto-implemented properties.
I came across this question during a code review today, and when I asked around my peer group, we could not come to a consensus either. I dislike ambiguity, and I wanted to know at least one burning question that would answer you question:
- Is there a performance gain or hit using one way over another?
Like finding out how many licks it takes to get to the center of a Tootsie Roll Tootsie Pop, I decided "lets find out".
Lets first actually do an apples to apples comparison.
Suppose we have two classes:
public class C
{
private int z;
public int Z
{
get { return z;}
}
}
public class Q
{
public int Z { get; }
}
The first class is a manual backing store, the second the auto compiler generated version.
Lets take a look at the IL generated for each.
First, the manual backing store version:
// Fields
.field private int32 z
// Methods
.method public hidebysig specialname
instance int32 get_Z () cil managed
{
// Method begins at RVA 0x2050
// Code size 12 (0xc)
.maxstack 1
.locals init (
[0] int32
)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld int32 C::z
IL_0007: stloc.0
IL_0008: br.s IL_000a
IL_000a: ldloc.0
IL_000b: ret
} // end of method C::get_Z
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2068
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [System.Private.CoreLib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method C::.ctor
// Properties
.property instance int32 Z()
{
.get instance int32 C::get_Z()
}
Now lets look at the IL for the second class:
// Fields
.field private initonly int32 '<Z>k__BackingField'
.custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01 00 00 00
)
.custom instance void [System.Private.CoreLib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [System.Private.CoreLib]System.Diagnostics.DebuggerBrowsableState) = (
01 00 00 00 00 00 00 00
)
// Methods
.method public hidebysig specialname
instance int32 get_Z () cil managed
{
.custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x2071
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld int32 Q::'<Z>k__BackingField'
IL_0006: ret
} // end of method Q::get_Z
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2068
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [System.Private.CoreLib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method Q::.ctor
// Properties
.property instance int32 Z()
{
.get instance int32 Q::get_Z()
}
Ignoring the extra compiler generated code for adding debuggable attributes, which add no appreciable executable code, there does not appear to be any difference in the generated code.
Now, you might argue that your question is not answered, but consider...
If you have ever coded properties that participate in binding, such as:
private string name;
public string Name
{
get { return name; }
set { SetProperty (ref name, value);
}
Then the backing store is the way to go.
On the other hand, using the "prop" <TAB><TAB> shortcut in the Visual Studio editor to generate the auto properties is just super handy, and is much faster than all that typing ;)
So in the end, to the hammer, everything looks like a nail. Don't be a hammer.
might
have to. Why waste time doing something which is prone to error (you will probably end up copying & pasting anyway), when you can do it only when needed? – Rob♦prop
and press tab. This results in an auto-implemented property. – cwharris