13

I follow a pattern when setting certain properties whereby I check to see if the corresponding field is empty, returning the field if not and setting it if so. I frequently use this for reading configuration settings, for example, so that the setting is read lazily and so that it is only read once. Here is an example:

private string DatabaseId
{
    get
    {
        if (string.IsNullOrEmpty(databaseId))
        {
            databaseId = CloudConfigurationManager.GetSetting("database");
        }

        return databaseId;
    }
}

I have started to use C# 6 autoproperty initialization as it really cleans up and makes my code more concise. I would like to do something like this:

private string DatabaseId { get; } = CloudConfigurationManager.GetSetting("database");

But I'm not sure how the compiler interprets it in this case. Will this have the same effect as my first block of code, setting the (automatically implemented) field once, and thereafter reading from the field? Or will this call the CloudConfigurationManager every time I get DatabaseId?

08Dc91wk
  • 4,254
  • 8
  • 34
  • 67

4 Answers4

14

What you show:

private string DatabaseId { get; } = CloudConfigurationManager.GetSetting("database");

Is an "Auto-Property Initializer", keyword being "initializer", from MSDN Blogs: C# : The New and Improved C# 6.0:

The auto-property initializer allows assignment of properties directly within their declaration. For read-only properties, it takes care of all the ceremony required to ensure the property is immutable.

Initializers run once per instance (or once per type for static members). See C# Language Specification, 10.4.5 Variable initializers:

For instance fields, variable initializers correspond to assignment statements that are executed when an instance of the class is created.

So that code compiles to something like this:

public class ContainingClass
{
    private readonly string _databaseId;
    public string DatabaseId { get { return _databaseId; } }

    public ContainingClass()
    {
        _databaseId = CloudConfigurationManager.GetSetting("database");
    }       
}

For static variables, this kind of looks the same:

private static string DatabaseId { get; } = CloudConfigurationManager.GetSetting("database");

Compiles to, more or less:

public class ContainingClass
{
    private static readonly string _databaseId;
    public static string DatabaseId { get { return _databaseId; } }

    static ContainingClass()
    {
        _databaseId = CloudConfigurationManager.GetSetting("database");
    }       
}

Though not entirely, as when the type doesn't have a static constructor, "static field initializers are executed at an implementation-dependent time prior to the first use of a static field of that class".

CodeCaster
  • 147,647
  • 23
  • 218
  • 272
10

C# 6.0 readonly auto property will create a field and invoke the initializer only once.

However, that is not equal to what you have there. In your code, CloudConfigurationManager.GetSetting will be called only when someone reads the DatabaseId property but with "readonly auto property" CloudConfigurationManager.GetSetting will be called at the time of class initialization itself.

This difference may/mayn't matter. It depends. If the call is expensive then you can use Lazy<T> which is roughly equal to what you have.

Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
2

It will set the value only once and after that just read it.

However there's a slight difference in the sense that you now no longer have a databaseId field. In your first example you basically check for id == null || id == "" to set the database string. That means that if you create a new instance with databaseId set to an empty string, the first example will still get the ID from the settings.

The second example however will see that empty string as a valid value and remain with it.

First code:

if(id == null || id == "") // Get ID from settings

Second code:

if(id == null) // Get ID from settings
Jeroen Vannevel
  • 43,651
  • 22
  • 107
  • 170
1

An auto-property has automatically a backing field. In this case, this field will only be assignable from the constructor or from the auto-property initializer. Your new code is better than the first one. It will only make one call to CloudConfigurationManager.GetSetting("database");. In the first example, you have to make a check every time your property get is called.

Philippe Paré
  • 4,279
  • 5
  • 36
  • 56