Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSharpNotesForProfessionals.pdf
Скачиваний:
57
Добавлен:
20.05.2023
Размер:
6.12 Mб
Скачать

Chapter 39: Constructors and Finalizers

Constructors are methods in a class that are invoked when an instance of that class is created. Their main responsibility is to leave the new object in a useful and consistent state.

Destructors/Finalizers are methods in a class that are invoked when an instance of that is destroyed. In C# they are rarely explicitely written/used.

Section 39.1: Static constructor

A static constructor is called the first time any member of a type is initialized, a static class member is called or a static method. The static constructor is thread safe. A static constructor is commonly used to:

Initialize static state, that is state which is shared across di erent instances of the same class. Create a singleton

Example:

class Animal

{

//* A static constructor is executed only once,

//when a class is first accessed.

//* A static constructor cannot have any access modifiers

//* A static constructor cannot have any parameters static Animal()

{

Console.WriteLine("Animal initialized");

}

//Instance constructor, this is executed every time the class is created public Animal()

{

Console.WriteLine("Animal created");

}

public static void Yawn()

{

Console.WriteLine("Yawn!");

}

}

var turtle = new Animal(); var giraffe = new Animal();

Output:

Animal initialized

Animal created

Animal created

View Demo

If the first call is to a static method, the static constructor is invoked without the instance constructor. This is OK, because the static method can't access instance state anyways.

GoalKicker.com – C# Notes for Professionals

153

Animal.Yawn();

This will output:

Animal initialized

Yawn!

See also Exceptions in static constructors and Generic Static Constructors .

Singleton example:

public class SessionManager

{

public static SessionManager Instance;

static SessionManager()

{

Instance = new SessionManager();

}

}

Section 39.2: Singleton constructor pattern

public class SingletonClass

{

public static SingletonClass Instance { get; } = new SingletonClass();

private SingletonClass()

{

// Put custom constructor code here

}

}

Because the constructor is private, no new instances of SingletonClass can be made by consuming code. The only way to access the single instance of SingletonClass is by using the static property SingletonClass.Instance.

The Instance property is assigned by a static constructor that the C# compiler generates. The .NET runtime guarantees that the static constructor is run at most once and is run before Instance is first read. Therefore, all synchronization and initialization concerns are carried out by the runtime.

Note, that if the static constructor fails the Singleton class becomes permanently unusable for the life of the AppDomain.

Also, the static constructor is not guaranteed to run at the time of the first access of Instance. Rather, it will run at some point before that. This makes the time at which initialization happens non-deterministic. In practical cases the JIT often calls the static constructor during compilation (not execution) of a method referencing Instance. This is a performance optimization.

See the Singleton Implementations page for other ways to implement the singleton pattern.

Section 39.3: Default Constructor

When a type is defined without a constructor:

GoalKicker.com – C# Notes for Professionals

154

public class Animal

{

}

then the compiler generates a default constructor equivalent to the following:

public class Animal

{

public Animal() {}

}

The definition of any constructor for the type will suppress the default constructor generation. If the type were defined as follows:

public class Animal

{

public Animal(string name) {}

}

then an Animal could only be created by calling the declared constructor.

// This is valid

var myAnimal = new Animal("Fluffy");

// This fails to compile

var unnamedAnimal = new Animal();

For the second example, the compiler will display an error message:

'Animal' does not contain a constructor that takes 0 arguments

If you want a class to have both a parameterless constructor and a constructor that takes a parameter, you can do it by explicitly implementing both constructors.

public class Animal

{

public Animal() {} //Equivalent to a default constructor. public Animal(string name) {}

}

The compiler will not be able to generate a default constructor if the class extends another class which doesn't have a parameterless constructor. For example, if we had a class Creature:

public class Creature

{

public Creature(Genus genus) {}

}

then Animal defined as class Animal : Creature {} would not compile.

Section 39.4: Forcing a static constructor to be called

While static constructors are always called before the first usage of a type it's sometimes useful to be able to force them to be called and the RuntimeHelpers class provide an helper for it:

GoalKicker.com – C# Notes for Professionals

155

using System.Runtime.CompilerServices;

// ...

RuntimeHelpers.RunClassConstructor(typeof(Foo).TypeHandle);

Remark: All static initialization (fields initializers for example) will run, not only the constructor itself.

Potential usages: Forcing initialization during the splash screen in an UI application or ensuring that a static constructor doesn't fail in an unit test.

Section 39.5: Calling a constructor from another constructor

public class Animal

{

public string Name { get; set; }

public Animal() : this("Dog")

{

}

public Animal(string name)

{

Name = name;

}

}

var dog = new Animal(); // dog.Name will be set to "Dog" by default.

var cat = new Animal("Cat"); // cat.Name is "Cat", the empty constructor is not called.

Section 39.6: Calling the base class constructor

A constructor of a base class is called before a constructor of a derived class is executed. For example, if Mammal extends Animal, then the code contained in the constructor of Animal is called first when creating an instance of a

Mammal.

If a derived class doesn't explicitly specify which constructor of the base class should be called, the compiler assumes the parameterless constructor.

public class Animal

{

public Animal() { Console.WriteLine("An unknown animal gets born."); } public Animal(string name) { Console.WriteLine(name + " gets born"); }

}

public class Mammal : Animal

{

public Mammal(string name)

{

Console.WriteLine(name + " is a mammal.");

}

}

In this case, instantiating a Mammal by calling new Mammal("George the Cat") will print

An unknown animal gets born.

George the Cat is a mammal.

GoalKicker.com – C# Notes for Professionals

156