The first approach you mention was the only way properties could be written until .NET 3.5.
The second one is called Auto-Implemented Property and was introduces with C# 3.0/.NET 3.5.
Internally, also the auto-implemented property uses a private variable to store the data. It´s only syntactic sugar added by the compiler to save the developer a bit of time and make the code more readable.
Why to use a visible declared variable instead of using just the auto-implemented property?
Well, this depends on the context of your program. If you just want a 1:1 public mapper for a name variable you can go with the auto-implementation.
If, however, there is some logic needed when setting/getting the value of the variable you have to use the extra variable approach.
Assume you got the requirement that the Names in your app must not be greater than 50 chars. You could handle this validation in the setter of your property:
Set(ByVal value As String)
If value.Length <= 50
_Name = value
Else
'Throw some validation error
End if
End Set
This can only be done if you defined an variable (here _Name). It´s not possible to do that with an auto-implemented property.
If you are interested in details:
Both of your code snippets produce (almost) the same MSIL:
Person.get_Name:
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld UserQuery+Person._Name
IL_0007: stloc.0 // Name
IL_0008: br.s IL_000A
IL_000A: ldloc.0 // Name
IL_000B: ret
Person.set_Name:
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldarg.1
IL_0003: stfld UserQuery+Person._Name
IL_0008: nop
IL_0009: ret
vs.
Person.get_Name:
IL_0000: ldarg.0
IL_0001: ldfld UserQuery+Person._Name
IL_0006: stloc.0
IL_0007: br.s IL_0009
IL_0009: ldloc.0
IL_000A: ret
Person.set_Name:
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: stfld UserQuery+Person._Name
IL_0007: nop
IL_0008: ret
As you can see both snippets read/write their string values in a varibale _Name (stfld).
(The only diff is the nop command which is used while debugging and can be ignored here.)
ReadOnlyproperties.