Section 40.2: public 액세스 한정자

public 키워드는 클래스 (중첩된 형식 포함), 속성, 메소드나 필드를 모든 사용자 (consumer) 들에게 직접 접근 가능하도록 허용한다:

public class Foo() { public string SomeProperty { get; set; } public class Baz { public int Value { get; set; } } } public class Bar() { public Bar() { var myInstance = new Foo(); var someValue = foo.SomeProperty; var myNestedInstance = new Foo.Baz(); var otherValue = myNestedInstance.Value; } }
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 40.1: 다이어그램으로 표현된 각 액세스 한정자 (access modifier) 들

이 장에서는 모든 액세스 한정자 (access modifier) 들이 가장 제한된 한정자부터 더욱 많은 접근을 허용하는 순서대로 벤다이어그램을 이용하여 표현되어 있다:

액세스 한정자 다이어그램
private private
internal internal
protected protected
protected internal protected internal
public public

이후에 이어지는 장들에서 각 액세스 한정자들에 대한 더욱 자세한 정보를 확인할 수 있다.

본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 39.11: 생성자에서 가상 메소드 호출하기

C++ 과 다르게, C# 에서는 클래스의 생성자에서 가상 메소드를 호출할 수 있다 (물론, 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 는 AnotherDerived 타입이다

기반 (base) 클래스의 생성자에서 이미 파생 클래스의 가상 메소드 테이블에 접근이 가능하다는 점이 C++ 에서 넘어온 사람들에게는 매우 놀라운 일일 수 있다.

유의할 점: 파생 클래스는 해당 시점에 아직 완전히 초기화가 이루어진 상태가 아닐 수 있다. (파생 클래스의 생성자는 기반 클래스의 생성자 다음으로 실행된다) 따라서 이러한 방식의 사용은 위험하다고 할 수있다 (StyleCop 에서는 이에 대한 경고도 포함하고 있다). 일반적으로 이는 바람직하지 않은 사용 예 (bad practice) 로 간주된다.

본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 39.10: Generic 타입의 Static 생성자

만약 static 생성자가 generic 타입에 대해 선언되어 있다면, 해당 static 생성자는 generic 인자들의 매 고유한 조합 (unique combination) 들에 대해 딱 한번씩만 호출될 것이다.

class Animal<T> { static Animal() { Console.WriteLine(typeof(T).FullName); } public static void Yawn() { } } Animal<Object>.Yawn(); Animal<String>.Yawn();

위 코드의 실행 결과는 다음과 같다:

System.Object System.String

Stack Overflow 의 다음 링크도 참조하도록 한다. "generic 타입들에 대한 static 생성자의 동작 방식"

본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 39.9: 생성자와 속성값 초기화 순서

속성값의 할당이 클래스의 생성자보다 먼저 이루어지는지, 아니면 나중에 이루어지는지를 알아보도록 한다.

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); // 최종 결과는 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" 이 된다

본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 39.8: Static 생성자에서의 예외 발생시의 동작

만약 static 생성자가 예외를 발생시킨다면, 이는 이후 다시 재시도되지 않는다. 따라서, 해당 타입 자체가 앱 도메인의 실행 기간 전체에 걸쳐 사용 불가능하게 된다. 이후로 해당 타입을 사용하고자 하는 모든 시도는 원래의 예외를 감싸고 있는 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()); }

위 코드는 다음과 같은 결과를 출력할 것이다:

Static ctor System.TypeInitializationException: The type initializer for 'Animal' threw an exception. ---> System.Exception: Exception of type 'System.Exception' was thrown. [...] System.TypeInitializationException: The type initializer for 'Animal' threw an exception. ---> System.Exception: Exception of type 'System.Exception' was thrown.

여기서 실제 생성자는 단 한번만 실행이 되며, 이후에는 해당 예외가 재사용될 것이다.

본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 39.7: 파생된 클래스들의 종료자 (Finalizer) 호출

개체 그래프에 대한 사용이 종료될 때 (finalized), 그 순서는 생성시의 반대가 된다. 예) 파생 클래스의 종료자가 먼저 수행되고 이후 기본 클래스의 종료자가 수행된다. 다음 코드 예제를 확인한다:

class TheBaseClass { ~TheBaseClass() { Console.WriteLine("Base class finalized!"); } } class TheDerivedClass: TheBaseClass { ~TheDerivedClass() { Console.WriteLine("Derived class finalized!"); } } // 객체가 접근 불가능한 상태가 되도록 만들기 위해 // 인스턴스를 변수에 할당하지 않는다. new TheDerivedClass(); // 예제가 동작하는 방식을 보여주기 위한 코드 // 다른 경우에는 사용하길 권하지 않는다. GC.Collect(); // Derived class finalized! // Base class finalized!
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 39.6: 기본 (base) 클래스의 생성자 호출하기

기본 (base) 클래스의 생성자는 파생 (derived) 클래스의 생성자가 불리기 전에 호출된다. 예를 들어, 만약 Mammal 클래스가 Animal 클래스를 확장 (extend) 한다면, Mammal 클래스의 인스턴스 생성 시 Animal 클래스의 생성자에 기술된 코드들이 먼저 실행된다.

만약 파생 클래스가 기본 클래스의 어느 생성자를 불러야 하는지 명시적으로 기술해 놓지 않았다면, 컴파일러는 기본적으로 파라미터가 없는 생성자를 부르는 것으로 간주한다.

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

이 경우에, Mammal 클래스의 인스턴스를 new Mammal("George the Cat") 코드를 통해 생성한다면 다음과 같은 결과가 출력될 것이다.

An unknown animal gets born. George the Cat is a mammal.

데모 확인하기

기본 클래스의 다른 생성자를 호출하고자 한다면 생성자의 서명 (signature) 부분과 본문 (body) 부분 사이에 : 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 gets born. George the Cat is a mammal.

데모 확인하기

본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

+ Recent posts