简体   繁体   中英

C# - Difference in behavior when auto properties are given default values vs accessors

I am trying to understand why the order of initialisation changes the values here. Shouldn't the accessor of the property return the value specified i/o the default. Thanks.

void Main()
{
    Console.WriteLine(StartDate);
    Console.WriteLine(EndDate);
}

private static DateTime StartDate { get; } = new DateTime(EndDate.Year, 1, 1);

private static DateTime EndDate { get; } = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1).AddDays(-1);

This prints

1/1/0001 12:00:00 AM
31/12/2018 12:00:00 AM

While

void Main()
{
    Console.WriteLine(StartDate);
    Console.WriteLine(EndDate);
}

private static DateTime EndDate { get; } = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1).AddDays(-1);

private static DateTime StartDate { get; } = new DateTime(EndDate.Year, 1, 1);

prints

1/1/2018 12:00:00 AM
31/12/2018 12:00:00 AM

If I change the properties to

private static DateTime EndDate { get => new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1).AddDays(-1); }

private static DateTime StartDate { get => new DateTime(EndDate.Year, 1, 1); }

or

private static DateTime StartDate => new DateTime(EndDate.Year, 1, 1);

private static DateTime EndDate => new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1).AddDays(-1);

I get consistent values regardless of the order in which they are specified.

There are two parts to this.

First, .NET is translating your first sample into a static constructor, initializing each variable in the order that they were declared.

Second, all class fields in .NET are initialized to their default value prior to your code running.

So when you use EndDate it has been set to its default value but your initializer for it hasn't yet run. You're accessing its default value. Essentially, you're getting this code generated:

class App
{
    static readonly DateTime _startDate, _endDate;

    static DateTime StartDate => _startDate;
    static DateTime EndDate => _endDate;

    static App()
    {
        // this code is put here implicitly by .NET

        _startDate = default;
        _endDate = default;

        // and this code is put here by C#,
        // translated from your initializers,
        // in the order they were declared.

        _startDate = new DateTime(_endDate.Year, 1, 1);
        _endDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1).AddDays(-1);
    }
}

Static initialisers are executed in the order that they are given in code and execute only once , hence why the first code block sees the EndDate property as a default value for DateTime (ie 1/1/0001 12:00:00 AM ).

The last code block doesn't have initialisers, it's an expression-bodied member which is really shorthand for a full blown method that executes every time you call the property . For example:

private static DateTime StartDate
{
    get
    {
        return new DateTime(EndDate.Year, 1, 1); 
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM