Section 43.2: Lazy<T> 를 이용한 지연된 (lazy) 방식의 스레드-안전한 Singleton

.NET 4.0 의 Lazy 타입은 스레드-안전한 방식의 객체 초기화를 보장하므로, 이를 Singleton 생성에 활용할 수 있다.

public class LazySingleton { private static readonly Lazy < LazySingleton > _instance = new Lazy < LazySingleton > (() => new LazySingleton()); public static LazySingleton Instance { get { return _instance.Value; } } private LazySingleton() {} }

Lazy<T> 는 실제 호출이 일어나는 시점에 객체가 생성됨을 보장한다.

실제 사용하는 예제는 다음과 같을 것이다:

using System; public class Program { public static void Main() { var instance = LazySingleton.Instance; } }

.NET Fiddle 에서 라이브 데모 확인하기

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

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

반응형

Section 43.1: 정적 초기화 방식의 Singleton

public class Singleton { private readonly static Singleton instance = new Singleton(); private Singleton() { } public static Singleton Instance => instance; }

위 구현은 instance 객체가 정적 생성자에서 초기화되므로 스레드 안전 (thread-safe) 하다. CLR 에서는 이미 모든 정적 생성자가 스레드 안전한 방식으로 실행됨을 보장하고 있다. instance 값을 변경하는 것은 스레드 안전한 조작이 아니므로, readonly 속성을 통해 초기화 시점 이후부터는 변경 불가능함 (immutability) 을 보장한다.

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

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

반응형

Section 42.3: Static 키워드

static 키워드는 두가지 의미를 가진다:

  1. 각 객체 (object) 별로 다른 값을 가지는 것이 아닌, 클래스 전체에 대해 동일한 값을 가지게 된다.
  2. 정적 (static) 속성값과 메소드들은 별도의 인스턴스를 필요로 하지 않는다.
public class Foo { public Foo { Counter++; NonStaticCounter++; } public static int Counter { get; set; } public int NonStaticCounter { get; set; } } public class Program { static void Main(string[] args) { // 인스턴스를 생성한다. var foo1 = new Foo(); Console.WriteLine(foo1.NonStaticCounter); // 출력값은 "1" 이 될 것이다. // 다음 호출이 인스턴스에 대한 접근이 아닌 클래스 이름에 대해 이루어짐에 유의한다. Console.WriteLine(Foo.Counter); // 출력값은 "1" 역시 이 될 것이다. // 두번째 인스턴스를 생성한다. var foo2 = new Foo(); Console.WriteLine(foo2.NonStaticCounter); // 출력값은 "1" 이 될 것이다. Console.WriteLine(Foo.Counter); // 이제 출력값은 "2" 가 될 것이다. // 정적 속성값은 두 인스턴스 모두에서 증가되어 있으며, 이는 전체 클래스상에 지속적으로 유지될 것이다. } }
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

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

반응형

Section 42.2: 정적 (Static) 클래스의 수명 (lifetime)

정적 (static) 클래스는 실제 멤버 접근시점에 지연된 (lazy) 초기화를 수행하며, 어플리케이션 도메인이 유지되는 동안 수명을 같이한다.

void Main() { Console.WriteLine("Static classes are lazily initialized"); Console.WriteLine("The static constructor is only invoked when the class is first accessed"); Foo.SayHi(); Console.WriteLine("Reflecting on a type won't trigger its static .ctor"); var barType = typeof (Bar); Console.WriteLine("However, you can manually trigger it with System.Runtime.CompilerServices.RuntimeHelpers "); RuntimeHelpers.RunClassConstructor(barType.TypeHandle); } // 다른 메소드 및 클래스들을 여기에 선언한다 public static class Foo { static Foo() { Console.WriteLine("static Foo.ctor"); } public static void SayHi() { Console.WriteLine("Foo: Hi"); } } public static class Bar { static Bar() { Console.WriteLine("static Bar.ctor"); } }

실행 결과:

Static classes are lazily initialized The static constructor is only invoked when the class is first accessed static Foo.ctor Foo: Hi Reflecting on a type won't trigger its static .ctor However, you can manually trigger it with System.Runtime.CompilerServices.RuntimeHelpers  static Bar.ctor
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

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

반응형

Section 42.1: 정적 (Static) 클래스

하나의 클래스를 "static" 키워드를 이용하여 선언하는 경우, 아래와 같은 3가지의 효과를 갖게 된다:

  1. static 클래스에 대한 인스턴스를 생성할 수 없다 (이로 인해 기본 생성자 역시 제거된다)
  2. 클래스내의 모든 속성값들과 메소드들 역시 static 으로 선언되어야 한다.
  3. static 클래스는 sealed클래스로써, 다른 클래스가 상속받을 수 없다.

역주: 인스턴스 생성자는 선언할 수 없지만 Static 생성자는 여전히 선언할 수 있습니다. (https://nochoco-lee.tistory.com/647)

public static class Foo { // 해당 클래스가 인스턴스화 될 수 없으므로 생성자가 없음에 유의한다 public static int Counter { get; set; } public static int GetCount() { return Counter; } } public class Program { static void Main(string[] args) { Foo.Counter++; Console.WriteLine(Foo.GetCount()); // 출력 결과는 1 이 된다 // var foo1 = new Foo(); // Foo 클래스는 생성자를 가질 수 없으므로 위 코드는 에러릏 발생시킬 것이다 } }
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

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

반응형

Section 41.7: 명시적 구현 (Explicit Implementation) 을 이용해 멤버를 숨김 처리하기

인터페이스 사용 시, 실제로 제공하고자 하지도 않는 기능들로 인해 사용자의 클래스에 불필요하게 많은 멤버가 public 하게 추가될 때가 있다. 이러한 경우, 명시적 구현 (Explicit Implementation) 을 해결책으로 사용할 수 있다.

public interface IMessageService { void OnMessageReceive(); void SendMessage(); string Result { get; set; } int Encoding { get; set; } // 기타 등등 }

일반적으로 클래스가 이 인터페이스를 구현 (implement) 하는 방식은 다음과 같다.

public class MyObjectWithMessages : IMessageService { public void OnMessageReceive(){ } public void SendMessage(){ } public string Result { get; set; } public int Encoding { get; set; } }

위 예제에서 확인할 수 있듯이, 모든 멤버가 public 하게 공개되어 있다.

var obj = new MyObjectWithMessages(); // 아래의 메소드를 호출할 필요가 있는가? obj.OnMessageReceive();

답: 필요하지 않다. 그러나 이 함수가 public 하게 선언 될 필요가 없다 하더라도, 이를 단순히 private 으로 바꾸어 선언한다면 컴파일러가 에러를 발생시킬 것이다.

이러한 경우에 명시적 구현 (explicit implementation) 을 사용할 수 있다:

public class MyObjectWithMessages : IMessageService{ void IMessageService.OnMessageReceive() { } void IMessageService.SendMessage() { } string IMessageService.Result { get; set; } int IMessageService.Encoding { get; set; } }

이제 필요한 멤버를 구현 (implement) 은 하였으나 해당 클래스에서는 어떠한 멤버도 public 하게 외부에 노출되지 않게 하였다.

var obj = new MyObjectWithMessages(); /* MyObjectWithMessages 타입에는 존재하지 않는 멤버라 에러가 발생할 것이다. * 해당 함수를 "private" 하게 변경하였다고 생각할 수 있다. */ obj.OnMessageReceive();

이렇듯 명시적 구현을 사용한 상황에서 만약 해당 멤버를 사용해야 하는 경우가 발생한다면, 객체를 인터페이스 형식으로 캐스팅하여 접근 가능하다.

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

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

반응형

Section 41.6: 인터페이스 사용 목적

인터페이스는 해당 인터페이스를 사용하는 사용자와 인터페이스를 구현 (implement) 하는 클래스간에 정의된 계약 (contract) 과도 같다. 혹은 인터페이스를 객체가 수행할 수 있는 특정 기능들에 대한 선언이라고 생각할 수도 있다.

각기 다른 도형들을 나타내기 위한 IShape 인터페이스를 정의한다고 가정해 보자. 각 도형은 면적을 가질 수 있으므로, 해당 인터페이스 구현물들이 각자의 면적을 공통적으로 반환하도록 하는 메소드를 다음과 같이 정의할 수 있다:

public interface IShape { double ComputeArea(); }

그런 다음, 다음과 같이 RectangleCircle 두 도형을 정의한다고 생각해 보자:

public class Rectangle: IShape { private double length; private double width; public Rectangle(double length, double width) { this.length = length; this.width = width; } public double ComputeArea() { return length * width; } } public class Circle: IShape { private double radius; public Circle(double radius) { this.radius = radius; } public double ComputeArea() { return Math.Pow(radius, 2.0) * Math.PI; } }

위에서 정의한 둘은 자신의 면적을 계산하는 각자의 방법을 가지고 있지만, 이들은 모두 동일한 인터페이스를 구현한 도형에 해당한다. 따라서, 코드상에서 이 둘을 동일한 IShape 으로 다루는 것은 논리적으로 아무런 문제가 없다 :

private static void Main(string[] args) { var shapes = new List < IShape > () { new Rectangle(5, 10), new Circle(5) }; ComputeArea(shapes); Console.ReadKey(); } private static void ComputeArea(IEnumerable < IShape > shapes) { foreach(shape in shapes) { Console.WriteLine("Area: {0:N}", shape.ComputeArea()); } } // Output: // Area : 50.00 // Area : 78.54
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

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

반응형

Section 41.5: 다중 인터페이스 구현 (implement) 하기

public interface IAnimal { string Name { get; set; } } public interface INoiseMaker { string MakeNoise(); } public class Cat: IAnimal, INoiseMaker { public Cat() { Name = "Cat"; } public string Name { get; set; } public string MakeNoise() { return "Nyan"; } }
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

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

반응형

+ Recent posts