2

I have the following piece of code:

// using Windows.Storage;
internal static class AppData {
    private static ApplicationDataContainer container;
    public static bool FirstTime { get; } = !GetContainer("UserInfo").Values.ContainsKey("complete");
    static AppData() {
        container = ApplicationData.Current.LocalSettings;
    }
    private static ApplicationDataContainer GetContainer(string name) {
        return container.CreateContainer(name,ApplicationDataCreateDisposition.Always);
    }
}

NullReferenceException: Object reference not set to an instance of an object.

I don't know why it's wrong. Make some changes to the code

// using Windows.Storage;
internal static class AppData {
    private static ApplicationDataContainer container;
    public static bool FirstTime => !GetContainer("UserInfo").Values.ContainsKey("complete");
    static AppData() {
        container = ApplicationData.Current.LocalSettings;
    }
    private static ApplicationDataContainer GetContainer(string name) {
        return container.CreateContainer(name,ApplicationDataCreateDisposition.Always);
    }
}

OK, No error.

Why?

sndai
  • 21
  • 6
  • 2
    Did you check if ApplicationData is null or ApplicationData.Current is null ? – Praneet Nadkar Mar 19 '18 at 12:06
  • 2
    This is not really a null reference question. In the first code chunk you have a property initializer, in the second you have a function definition. – BlackICE Mar 19 '18 at 12:10
  • 3
    The first version sets up a read-only property (with backing field) and assigns to the backing field at static construction time. The second sets up a property (with just a getter) _without a backing field_. The second will work because, by the time you call it, `container` won't be `null`. – mjwills Mar 19 '18 at 12:10
  • 2
    Possible duplicate of [Why static fields initialization occurs before the static constructor?](https://stackoverflow.com/questions/8285168/why-static-fields-initialization-occurs-before-the-static-constructor) – mjwills Mar 19 '18 at 12:26

2 Answers2

6

I'll have to look up the reference but the issue is that

public static bool FirstTime { get; } = ....;

is an initializer. And as an initializer it is executed just before the constructor.

When you change it to a function (or readonly lambda property), it is a normal member and it gets executed after the constructor.

H H
  • 263,252
  • 30
  • 330
  • 514
  • Here is that reference to save you lookup time: https://stackoverflow.com/a/8285210/5311735 – Evk Mar 19 '18 at 12:16
  • 1
    I second @mjwills. "=>" will make a read-only Prop without backing field, won't it? – Fildor Mar 19 '18 at 12:16
  • 1
    @Fildor it's a read-only property indeed. But read-only property is basically one function (like `GetFirstTime`), so distinction doesn't matter for this quesion. Still it's property and will be for example returned by `Type.GetProperty("FirstTime")`. – Evk Mar 19 '18 at 12:23
  • 2
    But you're right about the property, it is `bool FirstIme => ...` and a method would look like `bool FirstIme() => ...`. Semantics are the same. – H H Mar 19 '18 at 12:30
4

There is a big difference in the two syntaxes:

public static bool Property { get; set; } = true;

This is a property initialization syntax and is evaluated before any constructor runs. The property can have a setter as well here.

public static bool Property => true;

This is an expression property, get-only and the right hand side is evaluated only when it is called. This also means that it is evaluated each time it is accessed, so it is not a good design choice to use a complex method call on the right-hand side for example.

Also if you want to have the expression syntax while ensuring the right-hand side will be evaluated only when actually needed and only once, you can combine this syntax with a private field:

private static bool? _property;

public static bool Property => _property ?? ( _property = CalculateProperty() );

This will first check if the private backing field is null and if yes, initialize it, while the assignment itself will then return the assigned value as well.

Martin Zikmund
  • 38,440
  • 7
  • 70
  • 91