dotnet / docs

This repository contains .NET Documentation.
https://learn.microsoft.com/dotnet
Creative Commons Attribution 4.0 International
4.24k stars 5.88k forks source link

The documentation of the order of static constructors is wrong. #41629

Closed steinbitglis closed 2 months ago

steinbitglis commented 3 months ago

Type of issue

Other (describe below)

Description

The following bit seems wrong.

  1. Base static constructors run. Any static constructors, starting with Object.Object through each base class to the direct base class.
  2. The static constructor runs. The static constructor for the type runs.

As I will demonstrate, in cases where the members of the base class is not accessed by the deriving class, the deriving class constructor will run before the base constructor.

using static System.Console;

class BaseClass {
    static protected int c = 1;
    static BaseClass() => WriteLine($"{c++}. Static   constructor of *BaseClass*    called.");
    public BaseClass() => WriteLine($"{c++}. Instance constructor of *BaseClass*    called.");
}
class DerivedClass : BaseClass {
    static DerivedClass()          => WriteLine($"{c++}. Static   constructor of  DerivedClass  called.");
    public DerivedClass() : base() => WriteLine($"{c++}. Instance constructor of  DerivedClass  called.");
}

public static class C {
    public static int c = 1;
}
class BaseClass2 {
    static BaseClass2() => WriteLine($"{C.c++}. Static   constructor of *BaseClass2*   called.");
    public BaseClass2() => WriteLine($"{C.c++}. Instance constructor of *BaseClass2*   called.");
}
class DerivedClass2 : BaseClass2 {
    static DerivedClass2()          => WriteLine($"{C.c++}. Static   constructor of  DerivedClass2 called.");
    public DerivedClass2() : base() => WriteLine($"{C.c++}. Instance constructor of  DerivedClass2 called.");
}

public class Program {
    public static void Main() {
        var instance = new DerivedClass();
        WriteLine();
        var instance2 = new DerivedClass2();
    }
}

The results are different in the two cases:

1. Static   constructor of *BaseClass*    called.
2. Static   constructor of  DerivedClass  called.
3. Instance constructor of *BaseClass*    called.
4. Instance constructor of  DerivedClass  called.

1. Static   constructor of  DerivedClass2 called.
2. Static   constructor of *BaseClass2*   called.
3. Instance constructor of *BaseClass2*   called.
4. Instance constructor of  DerivedClass2 called.

Page URL

https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-constructors

Content source URL

https://github.com/dotnet/docs/blob/main/docs/csharp/programming-guide/classes-and-structs/static-constructors.md

Document Version Independent Id

ac4f6361-70a7-fb43-101e-c12044f60896

Article author

@BillWagner

Metadata


Associated WorkItem - 275698

BillWagner commented 3 months ago

Here's a related interesting bit:

using static System.Console;

class BaseClass {
    static protected int c = 1;
    static BaseClass() => WriteLine($"{c++}. Static   constructor of *BaseClass*    called.");
    public BaseClass() => WriteLine($"{c++}. Instance constructor of *BaseClass*    called.");
}
class DerivedClass : BaseClass {
    static DerivedClass()          => WriteLine($"{c++}. Static   constructor of  DerivedClass  called.");
    public DerivedClass() : base() => WriteLine($"{c++}. Instance constructor of  DerivedClass  called.");
}

public static class C {
    public static int c = 1;
}
class BaseClass2 {
    static BaseClass2() => WriteLine($"{C.c++}. Static   constructor of *BaseClass2*   called.");
    public BaseClass2() => WriteLine($"{C.c++}. Instance constructor of *BaseClass2*   called.");
}
class DerivedClass2 : BaseClass {
    static DerivedClass2()          => WriteLine($"{C.c++}. Static   constructor of  DerivedClass2 called.");
    public DerivedClass2() : base() => WriteLine($"{C.c++}. Instance constructor of  DerivedClass2 called.");
}

public class Program {
    public static void Main() {
        var instance = new DerivedClass();
        WriteLine();
        var instance2 = new DerivedClass2();
    }
}

The output is:

1. Static   constructor of *BaseClass*    called.
2. Static   constructor of  DerivedClass  called.
3. Instance constructor of *BaseClass*    called.
4. Instance constructor of  DerivedClass  called.

1. Static   constructor of  DerivedClass2 called.
5. Instance constructor of *BaseClass*    called.
2. Instance constructor of  DerivedClass2 called.

The static ctor for BaseClass isn't required to be run at all. No static members have been accessed.

steinbitglis commented 3 months ago

Here's a related interesting bit: ... The static ctor for BaseClass isn't required to be run at all. No static members have been accessed.

I think you just mixed up BaseClass and BaseClass2 there.