수색…


소개

생성자는 해당 클래스의 인스턴스가 만들어 질 때 호출되는 클래스의 메서드입니다. 주요 책임은 새 객체를 유용하고 일관된 상태로 두는 것입니다.

소멸자 / 종결 자 (Destructors / Finalizers)는 클래스의 인스턴스가 소멸 될 때 호출되는 클래스의 메서드입니다. C #에서는 명시 적으로 작성 / 사용되는 경우는 거의 없습니다.

비고

C #에는 실제로 소멸자가 없지만 C ++ 스타일 소멸자 구문을 사용하는 Finalizers가 있습니다. 소멸자를 지정하면 직접 호출 할 수없는 Object.Finalize() 메서드가 재정의됩니다.

비슷한 구문을 가진 다른 언어와 달리 객체는 범위를 벗어 났을 때 호출 되지 않지만 특정 조건에서 발생하는 가비지 수집기가 실행될 때 호출됩니다. 따라서 특정 순서로 실행되는 것은 아닙니다 .

파이널 라이저는 관리되지 않는 리소스 (마샬 클래스를 통해 얻은 포인터, p / Invoke (시스템 호출) 또는 안전하지 않은 블록에서 사용되는 원시 포인터를 통해받은 포인터) 정리해야합니다. 관리 자원을 정리하려면 IDisposable, Dispose 패턴 및 using 문을 검토하십시오.

(추가 정보 : 언제 소멸자를 만들어야합니까? )

기본 생성자

유형이 생성자없이 정의되는 경우 :

public class Animal
{
}

컴파일러는 다음과 같은 기본 생성자를 생성합니다.

public class Animal
{
    public Animal() {}
}

유형에 대한 모든 생성자의 정의는 기본 생성자 생성을 억제합니다. 유형이 다음과 같이 정의 된 경우 :

public class Animal
{
    public Animal(string name) {}
}

Animal 은 선언 된 생성자를 호출해야만 생성 될 수 있습니다.

// This is valid
var myAnimal = new Animal("Fluffy");
// This fails to compile
var unnamedAnimal = new Animal();

두 번째 예제에서 컴파일러는 오류 메시지를 표시합니다.

'Animal'에는 0 개의 인수를 취하는 생성자가 포함되어 있지 않습니다.

클래스에 매개 변수없는 생성자와 매개 변수를 사용하는 생성자를 둘 다 원하면 두 생성자를 명시 적으로 구현하여 매개 변수를 생성 할 수 있습니다.

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

클래스가 매개 변수없는 생성자가없는 다른 클래스를 확장하면 컴파일러는 기본 생성자를 생성 할 수 없습니다. 예를 들어, 우리가 Creature 클래스를 가지고 있다면 :

public class Creature
{
    public Creature(Genus genus) {}
}

다음 Animal 로 정의 class Animal : Creature {} 컴파일되지 것입니다.

다른 생성자에서 생성자 호출

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.

정적 생성자

정적 생성자는 형식의 멤버가 처음 초기화되거나 정적 클래스 멤버가 호출되거나 정적 메서드가 호출 될 때마다 호출됩니다. 정적 생성자는 스레드로부터 안전합니다. 정적 생성자는 일반적으로 다음 작업을 수행하는 데 사용됩니다.

  • 정적 상태를 초기화합니다. 즉, 같은 클래스의 여러 인스턴스에서 공유되는 상태입니다.
  • 싱글 톤 만들기

예:

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();

산출:

동물 초기화 됨
창조 된 동물
창조 된 동물

데모보기

첫 x 째 호출이 정적 메소드에 대한 것이면 정적 생성자는 인스턴스 생성자없이 호출됩니다. 정적 메서드가 인스턴스 상태에 어쨌든 액세스 할 수 없기 때문에이 작업은 정상입니다.

Animal.Yawn();

그러면 다음과 같이 출력됩니다.

동물 초기화 됨
하품 소리!

정적 생성자일반 정적 생성자의 예외를 참조하십시오.

싱글 톤 예제 :

public class SessionManager
{
    public static SessionManager Instance;

    static SessionManager()
    {
        Instance = new SessionManager();
    }
}

기본 클래스 생성자 호출

기본 클래스의 생성자는 파생 클래스의 생성자가 실행되기 전에 호출됩니다. 예를 들어, Mammal 연장 Animal , 다음의 생성자에 포함 된 코드 Animal (A)의 인스턴스를 생성 할 때 제라고 Mammal .

파생 클래스가 기본 클래스의 생성자를 호출해야하는지 명시 적으로 지정하지 않으면 컴파일러에서 매개 변수없는 생성자를 사용합니다.

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.");
    }
}

이 경우 new Mammal("George the Cat") 을 호출하여 Mammal 을 인스턴스화하면

알 수없는 동물이 태어납니다.
George the Cat은 포유 동물입니다.

데모보기

기본 클래스의 다른 생성자를 호출하는 방법은 다음과 같습니다. 생성자의 서명과 본문 사이에 : base(args)

public class Mammal : Animal
{
    public Mammal(string name) : base(name)
    {
        Console.WriteLine(name + " is a mammal.");
    }
}

new Mammal("George the Cat") 을 호출하면 다음과 같이 인쇄됩니다.

조지 조지 고양이가 태어납니다.
George the Cat은 포유 동물입니다.

데모보기

파생 클래스의 종결 자

객체 그래프가 마무리되면 순서는 구조의 역순입니다. 예를 들어, 슈퍼 타입은 다음 코드와 같이 기본 타입보다 먼저 파이널 드됩니다.

class TheBaseClass
{
    ~TheBaseClass() 
    {
        Console.WriteLine("Base class finalized!");
    }
}

class TheDerivedClass : TheBaseClass
{
    ~TheDerivedClass() 
    {
        Console.WriteLine("Derived class finalized!");
    }
}

//Don't assign to a variable
//to make the object unreachable
new TheDerivedClass();

//Just to make the example work;
//this is otherwise NOT recommended!
GC.Collect();

//Derived class finalized!
//Base class finalized!

싱글 톤 생성자 패턴

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

    private SingletonClass()
    {
        // Put custom constructor code here
    }    
}

생성자가 private이기 때문에 코드를 사용하여 SingletonClass 새 인스턴스를 만들 수 없습니다. SingletonClass 의 단일 인스턴스에 액세스하는 유일한 방법은 static 속성 SingletonClass.Instance 사용하는 것입니다.

Instance 속성은 C # 컴파일러에서 생성하는 정적 생성자에 의해 지정됩니다. .NET 런타임은 정적 생성자가 한 번만 실행되고 Instance 가 처음 읽히기 전에 실행된다는 것을 보장합니다. 따라서 모든 동기화 및 초기화 문제는 런타임에 의해 수행됩니다.

정적 생성자가 실패하면 Singleton 클래스는 AppDomain의 수명 동안 영구적으로 사용할 수 없게됩니다.

또한 정적 생성자는 Instance 의 첫 번째 액세스시 실행되지 않을 수도 있습니다. 오히려, 그것은 어느 시점 이전에 실행될 것 입니다 . 이것은 초기화가 비 결정적이되는 시간을 만듭니다. 실제로 JIT는 Instance 참조하는 메소드의 컴파일 (실행 아님) 중에 정적 생성자를 호출하는 경우가 있습니다. 이것은 성능 최적화입니다.

싱글 톤 패턴을 구현하는 다른 방법은 싱글 톤 구현 페이지를 참조하십시오.

정적 생성자를 호출하도록 강제 실행

정적 생성자는 항상 타입의 첫 번째 사용 전에 호출되지만 강제로 호출 할 수있는 것이 유용 할 때도 있고 RuntimeHelpers 클래스가이를 지원합니다.

using System.Runtime.CompilerServices;    
// ...
RuntimeHelpers.RunClassConstructor(typeof(Foo).TypeHandle);

참고 : 모든 정적 초기화 (예 : 필드 이니셜 라이저)는 생성자 자체는 물론 실행되지 않습니다.

잠재적 인 사용법 : UI 응용 프로그램의 스플래시 화면에서 초기화를 강제 실행하거나 단위 테스트에서 정적 생성자가 실패하지 않도록합니다.

생성자에서 가상 메서드 호출

C #의 C ++과는 달리 클래스 생성자에서 가상 메서드를 호출 할 수 있습니다 (OK, C ++에서도 가능하지만 처음에는 동작이 놀랍습니다). 예 :

abstract class Base
{
    protected Base()
    {
        _obj = CreateAnother();
    }

    protected virtual AnotherBase CreateAnother()
    {
        return new AnotherBase();
    }

    private readonly AnotherBase _obj;
}

sealed class Derived : Base
{
    public Derived() { }

    protected override AnotherBase CreateAnother()
    {
        return new AnotherDerived();
    }
}

var test = new Derived();
// test._obj is AnotherDerived

C ++ 배경에서 왔을 때 놀랍습니다. 기본 클래스 생성자는 이미 파생 클래스 가상 메소드 테이블을 봅니다!

주의 : 파생 클래스가 아직 완전히 초기화되지 않았을 수 있습니다 (생성자가 기본 클래스 생성자 이후에 실행 됨).이 기법은 위험합니다 (이 경우 StyleCop 경고도 있음). 보통 이것은 나쁜 습관으로 간주됩니다.

일반 정적 생성자

정적 생성자가 선언 된 형식이 generic 인 경우 정적 생성자는 generic 인수의 고유 한 조합마다 한 번 호출됩니다.

class Animal<T>
{
    static Animal()
    {
        Console.WriteLine(typeof(T).FullName);
    }

    public static void Yawn() { }
}

Animal<Object>.Yawn();
Animal<String>.Yawn();

그러면 다음과 같이 출력됩니다.

System.Object
System.String

See also 어떻게 제네릭 타입의 정적 생성자가 작동합니까?

정적 생성자의 예외

정적 생성자가 예외를 throw하면 다시 시도되지 않습니다. 형식은 AppDomain의 수명 동안 사용할 수 없습니다. 이 유형을 사용하면 원래 예외를 감싸는 TypeInitializationException 발생합니다.

public class Animal
{
    static Animal()
    {
        Console.WriteLine("Static ctor");
        throw new Exception();
    }

    public static void Yawn() {}
}

try
{
    Animal.Yawn();
}
catch (Exception e)
{
    Console.WriteLine(e.ToString());
}

try
{
    Animal.Yawn();
}
catch (Exception e)
{
    Console.WriteLine(e.ToString());
}

그러면 다음과 같이 출력됩니다.

정적 ctor

System.TypeInitializationException : 'Animal'의 형식 이니셜 라이저에서 예외가 발생했습니다. ---> System.Exception : 'System.Exception'유형의 예외가 발생했습니다.

[...]

System.TypeInitializationException : 'Animal'의 형식 이니셜 라이저에서 예외가 발생했습니다. ---> System.Exception : 'System.Exception'유형의 예외가 발생했습니다.

여기서 실제 생성자는 한 번만 실행되고 예외는 다시 사용된다는 것을 알 수 있습니다.

생성자 및 속성 초기화

클래스의 생성자 이나 뒤에 속성 값의 할당을 실행 해야 합니까?

public class TestClass 
{
    public int TestProperty { get; set; } = 2;
    
    public TestClass() 
    {
        if (TestProperty == 1) 
        {
            Console.WriteLine("Shall this be executed?");
        }

        if (TestProperty == 2) 
        {
            Console.WriteLine("Or shall this be executed");
        }
    }
}

var testInstance = new TestClass() { TestProperty = 1 };

위의 예에서, 상기한다 TestProperty 값은 1 클래스 '생성자 또는 클래스 생성자 후를?


다음과 같이 인스턴스 생성시 속성 값 할당 :

var testInstance = new TestClass() {TestProperty = 1};

생성자가 실행 된 후에 실행됩니다. 그러나 C # 6.0의 클래스 '속성에서 속성 값을 다음과 같이 초기화합니다.

public class TestClass 
{
    public int TestProperty { get; set; } = 2;

    public TestClass() 
    {
    }
}

생성자가 실행 되기 전에 완료됩니다.


위의 두 개념을 하나의 예제로 결합하면 다음과 같습니다.

public class TestClass 
{
    public int TestProperty { get; set; } = 2;
    
    public TestClass() 
    {
        if (TestProperty == 1) 
        {
            Console.WriteLine("Shall this be executed?");
        }

        if (TestProperty == 2) 
        {
            Console.WriteLine("Or shall this be executed");
        }
    }
}

static void Main(string[] args) 
{
    var testInstance = new TestClass() { TestProperty = 1 };
    Console.WriteLine(testInstance.TestProperty); //resulting in 1
}

최종 결과:

"Or shall this be executed"
"1"

설명:

TestProperty 값은 먼저 2 로 지정되고 TestClass 생성자가 실행되어

"Or shall this be executed"

그런 다음 new TestClass() { TestProperty = 1 } 로 인해 TestProperty1 로 지정되고 Console.WriteLine(testInstance.TestProperty) 에 의해 인쇄 된 TestProperty 의 최종 값이됩니다.

"1"


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow