C# Language
생성자 및 종료 자
수색…
소개
생성자는 해당 클래스의 인스턴스가 만들어 질 때 호출되는 클래스의 메서드입니다. 주요 책임은 새 객체를 유용하고 일관된 상태로 두는 것입니다.
소멸자 / 종결 자 (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 }
로 인해 TestProperty
가 1
로 지정되고 Console.WriteLine(testInstance.TestProperty)
에 의해 인쇄 된 TestProperty
의 최종 값이됩니다.
"1"