Section 45.3: Partial 메소드

Partial 메소드은 한 partial 클래스 내의 선언부와 (주로 자동 생성되는 경우가 이에 해당한다) 또다른 partial 클래스 내의 구현부로 이루어진다.

using System; namespace PartialClassAndMethods { public partial class PartialClass // 자동 생성되는 코드 { partial void PartialMethod(); } public partial class PartialClass // 사용자가 직접 작성하는 코드 { public void PartialMethod() { Console.WriteLine("Partial method called."); } } class Program { static void Main(string[] args) { PartialClass partial = new PartialClass(); partial.PartialMethod(); // "Partial method called." 가 출력된다 } } }
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

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

반응형

Section 45.2: 기반 (Base) 클래스를 상속받는 partial class 작성하기

특정 기반 (base) 클래스를 상속받는 partial 클래스를 작성할 때에는, partial 클래스들 중 단 하나의 partial 클래스에만 기반 클래스를 명시하면 된다.

// PartialClass1.cs public partial class PartialClass : BaseClass {} // PartialClass2.cs public partial class PartialClass {}

물론 하나 이상의 partial 클래스에서 동일한 기반 클래스를 명시할 수도 있다. IDE 에 따라 이 부분이 불필요하게 중복되었다고 표시가 될 수는 있으나, 문제 없이 컴파일 됨을 알 수 있다.

// PartialClass1.cs public partial class PartialClass : BaseClass {} // PartialClass2.cs public partial class PartialClass : BaseClass {} // 같은 기반 클래스를 명시하는 것은 불필요한 중복이다

복수개의 partial 클래스들마다 각각 다른 기반 클래스를 기술하는 것은 허용되지 않는다. 이러한 경우 컴파일 에러가 발생할 것이다.

// PartialClass1.cs public partial class PartialClass : BaseClass {} // 컴파일 에러 // PartialClass2.cs public partial class PartialClass : OtherBaseClass {} // 컴파일 에러
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

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

반응형

Section 45.1: Partial 클래스

Partial 클래스는 클래스 선언을 분리 (주로 별도의 파일을 통해) 할 수 있는 기능을 제공한다. 일반적으로 자동 생성되는 코드를 수정하는 경우, 이후 코드가 다시 자동 생성될 때 사용자가 작성한 부분이 덮어써질 위험이 있으나, partial 클래스를 이용하면 이러한 문제를 해결할 수 있다. 또한 복수의 개발자들이 하나의 클래스나 메소드를 함께 개발하기에도 용이하다.

using System; namespace PartialClassAndMethods { public partial class PartialClass { public void ExampleMethod() { Console.WriteLine("Method call from the first declaration."); } } public partial class PartialClass { public void AnotherExampleMethod() { Console.WriteLine("Method call from the second declaration."); } } class Program { static void Main(string[] args) { PartialClass partial = new PartialClass(); partial.ExampleMethod(); // "Method call from the first declaration." 가 출력된다 partial.AnotherExampleMethod(); // "Method call from the second declaration." 가 출력된다 } } }
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

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

반응형

Chapter 45: Partial 클래스와 Partial 메소드

Partial 클래스를 이용하면 하나의 클래스를 여러 소스파일에 걸친 여러 부분 (part) 으로 분리할 수 있는 선택지를 제공한다. 모든 부분들은 컴파일 시점에 하나의 단일 클래스로 합쳐지게 된다.

모든 부분들은 partial 키워드를 포함하여야 하며, 동일한 액세스 한정자를 가져야 한다. 또한, 모든 부분이 컴파일 시점에 포함될 수 있도록 동일한 assembly 내에 존재하여야 한다.

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

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

반응형

Section 44.2: MEF 를 이용한 Dependency injection

public interface ILogger { void Log(string message); } [Export(typeof (ILogger))] [ExportMetadata("Name", "Console")] public class ConsoleLogger: ILogger { public void Log(string message) { Console.WriteLine(message); } } [Export(typeof (ILogger))] [ExportMetadata("Name", "File")] public class FileLogger: ILogger { public void Log(string message) { // 메시지를 파일에 기록한다 } } public class User { private readonly ILogger logger; public User(ILogger logger) { this.logger = logger; } public void LogUser(string message) { logger.Log(message); } } public interface ILoggerMetaData { string Name { get; } } internal class Program { private CompositionContainer _container; [ImportMany] private IEnumerable < Lazy < ILogger, ILoggerMetaData >> _loggers; private static void Main() { ComposeLoggers(); Lazy < ILogger, ILoggerMetaData > loggerNameAndLoggerMapping = _ loggers.First((n) => ((n.Metadata.Name.ToUpper() == "Console"))); ILogger logger = loggerNameAndLoggerMapping.Value; var user = new User(logger); user.LogUser("user name"); } private void ComposeLoggers() { // 복수개의 catalog 를 결합한 catalog 의 집합이다 var catalog = new AggregateCatalog(); string loggersDllDirectory = Path.Combine(Utilities.GetApplicationDirectory(), "Loggers"); if (!Directory.Exists(loggersDllDirectory)) { Directory.CreateDirectory(loggersDllDirectory); } // PluginManager 와 동일한 어셈블리 내에서 발견된 모든 part 들을 추가한다 catalog.Catalogs.Add(new AssemblyCatalog(typeof (Program).Assembly)); catalog.Catalogs.Add(new DirectoryCatalog(loggersDllDirectory)); // catalog 내의 part 들로 이루어진 CompositionContainer 를 생성한다 _container = new CompositionContainer(catalog); // 이 객체의 import 에 해당하는 부분을 채워넣는다 try { this._container.ComposeParts(this); } catch (CompositionException compositionException) { throw new CompositionException(compositionException.Message); } } }
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

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

반응형

Section 44.1: C# 과 ASP.NET 에서 Unity 를 통한 Dependency Injection 사용하기

우선적으로, 사용자의 코드에서 Dependency Injection 을 사용하여야 할 이유는 무엇일까? 이는 사용자의 프로그램 내 특정 구성 요소 (component) 들을 다른 클래스들로부터 분리 (decouple) 하기 위함이다. 다음과 같은 AnimalController 클래스가 있다고 가정을 해보자:

public class AnimalController() { private SantaAndHisReindeer _SantaAndHisReindeer = new SantaAndHisReindeer(); public AnimalController() { Console.WriteLine(""); } }

해당 코드 자체만 보면 아무런 문제가 없다고 생각할 수도 있겠지만, 이러한 경우 AnimalController_SantaAndHisReindeer 객체 자체에 의존성을 갖게 된다. 이로 인해 우리의 Controller 는 테스트하기 어려운 구조를 갖게 되며, 코드의 재사용성 역시 나빠지는 결과를 초래하게 된다.

Depedency Injection 과 인터페이스를 사용해야 하는 이유에 대해서는 이곳 에 설명이 상세히 되어 있다.

만약 Unity 가 DI (Dependency Injection) 를 처리하게 하고자 한다면, 해당 과정은 매우 간단하다. NuGet (패키지 관리자) 을 이용하여 손쉽게 기존 코드에 Unity 를 import 할 수 있다.

Visual Studio Tools -> NuGet Package Manager -> Manage Packages for Solution -> 검색창에서 Unity 입력 -> 프로젝트 선택 -> 설치

이제 주석이 잘 달려있는 두개의 파일이 생성될 것이다 : App-Data 폴더 내의 UnityConfig.csUnityMvcActivator.cs

UnityConfigRegisterTypes 메소드에서는, 사용자의 생성자에서 주입될 타입들을 확인할 수 있다.

namespace Vegan.WebUi.App_Start { public class UnityConfig { #region Unity Container private static Lazy < IUnityContainer > container = new Lazy < IUnityContainer > (() => { var container = new UnityContainer(); RegisterTypes(container); return container; }); /// <summary> /// Gets the configured Unity container. /// </summary> public static IUnityContainer GetConfiguredContainer() { return container.Value; } #endregion /// <summary>Registers the type mappings with the Unity container.</summary> /// <param name="container">The unity container to configure.</param> /// <remarks>There is no need to register concrete types such as controllers or API controllers (unless you want to /// change the defaults), as Unity allows resolving a concrete type even if it was not previously registered. < /remarks> public static void RegisterTypes(IUnityContainer container) { // NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements. // container.LoadConfiguration(); // TODO: Register your types here // container.RegisterType<IProductRepository, ProductRepository>(); container.RegisterType < ISanta, SantaAndHisReindeer > (); } } }

UnityMvcActivator -> 마찬가지로 해당 클래스가 Unity 를 ASP.NET MVC 와 연동시켜 줄 것이라는 상세한 주석을 확인할 수 있다.

using System.Linq; using System.Web.Mvc; using Microsoft.Practices.Unity.Mvc; [assembly: WebActivatorEx.PreApplicationStartMethod(typeof (Vegan.WebUi.App_Start.UnityWebActivator), "Start") ] [assembly: WebActivatorEx.ApplicationShutdownMethod(typeof (Vegan.WebUi.App_Start.UnityWebActivator), "Shutdown") ] namespace Vegan.WebUi.App_Start { /// <summary>Provides the bootstrapping for integrating Unity with ASP.NET MVC.</summary> public static class UnityWebActivator { /// <summary>Integrates Unity when the application starts.</summary> public static void Start() { var container = UnityConfig.GetConfiguredContainer(); FilterProviders.Providers.Remove(FilterProviders.Providers.OfType < FilterAttributeFilterProvider > ().First()); FilterProviders.Providers.Add(new UnityFilterAttributeFilterProvider(container)); DependencyResolver.SetResolver(new UnityDependencyResolver(container)); // TODO: Uncomment if you want to use PerRequestLifetimeManager // Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof (UnityPerRequestHttpModule)); } /// <summary>Disposes the Unity container when the application is shut down.</summary> public static void Shutdown() { var container = UnityConfig.GetConfiguredContainer(); container.Dispose(); } } }

이제 우리는 Controller 를 SantAndHisReindeer 클래스로부터 분리할 수 있다.

public class AnimalController() { private readonly SantaAndHisReindeer _SantaAndHisReindeer; public AnimalController(SantaAndHisReindeer SantaAndHisReindeer) { _SantAndHisReindeer = SantaAndHisReindeer; } }

이제 최종적으로 어플리케이션을 실행하기 전 수행해주어야 하는 마지막 작업이 남아있다.

Global.asax.cs 에서 다음과 같은 코드를 추가해 주어야 한다:

UnityWebActivator.Start()

이는 Unity 를 시작 및 구성하며 사용자의 타입들을 등록해 줄 것이다.

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using Vegan.WebUi.App_Start; namespace Vegan.WebUi { public class MvcApplication: System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); UnityWebActivator.Start(); } } }
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

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

반응형
CSharpNotes.43-4.md

Section 43.4: 지연된, 스레드-안전한 Singleton (.NET 3.5 나 이전 버전들을 위한 대체 구현)

.NET 3.5 나 그 이전버전에서는 Lazy<T> 클래스가 제공되지 않으므로, 다음과 같은 패턴을 대신하여 사용할 수 있다:

public class Singleton { private Singleton() // public 한 초기화 수행을 방지한다 {} public static Singleton Instance { get { return Nested.instance; } } private class Nested { // 해당 타입을 beforefieldinit 으로 표기하지 않도록 // 컴파일러에게 알려주기 위한 명시적 정적 생성자 static Nested() {} internal static readonly Singleton instance = new Singleton(); } }

이 구현은 Jon Skeet 의 블로그 포스팅 으로부터 영감을 받아 적성되었다.

위 예제에서 Nested 는 중첩 (nested) 된 private 클래스이므로 Sigleton 클래스의 다른 멤버들 (예들들어 public 한 읽기전용 속성값 등) 에 대한 접근으로 인해 Singleton 인스턴스의 초기화 작업이 시작되는 경우는 없을 것이다.

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

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

반응형

Section 43.3: 스레드-안전한 지연된 Singleton (Double Checked Locking - 이중 검사 잠금 방식)

여기서 소개하고 있는 스레드-안전한 버전의 singleton 은 .NET 초기 버전들에서 필수적이었는데, 이는 당시 정적 (static) 초기화 과정이 스레드-안전함을 보장하지 않았기 때문이다. 이러한 아래와 같은 패턴은 구현 과정에서 실수를 유발할 가능성이 높으므로, 근래의 버전들에서는 정적 초기화 방식의 singleton 사용이 주로 선호된다.

public sealed class ThreadSafeSingleton { private static volatile ThreadSafeSingleton instance; private static object lockObject = new Object(); private ThreadSafeSingleton() {} public static ThreadSafeSingleton Instance { get { if (instance == null) { lock(lockObject) { if (instance == null) { instance = new ThreadSafeSingleton(); } } } return instance; } } }

눈여겨 볼 부분은 if (instance == null) 로써, 해당 확인 과정이 잠금 (lock) 을 획득하기 전에 한번, 직후에 한번, 총 두번에 걸쳐 등장하고 있다.

첫번째 등장하는 null 확인 과정이 없다고 하더라도 해당 구현은 여전히 스레드-안전할 것이나, 이는 해당 인스턴스를 요청할 때마다 매번 잠금을 획득하여야 하며, 그로 인해 성능 저하가 발생할 수 있다는것을 의미한다. 첫번째 null 확인 과정이 추가됨으로써, 꼭 필요하지 않은 경우에는 잠금을 획득하지 않도록 할 수 있다. 두번째 null 확인 과정은 잠금을 획득한 첫번째 스레드 하나만이 인스턴스 생성을 수행할 수 있음을 보장해 준다. 이후의 스레드들은 잠금을 획득한다고 하더라도 인스턴스가 이미 생성되었음을 확인할 수 있으므로 그 다음에 위치한 생성 코드를 수행하지 않을 것이다.

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

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

반응형

+ Recent posts