Partial 메소드은 한 partial 클래스 내의 선언부와 (주로 자동 생성되는 경우가 이에 해당한다) 또다른 partial 클래스 내의 구현부로 이루어진다.
using System;
namespacePartialClassAndMethods {
publicpartialclassPartialClass// 자동 생성되는 코드
{
partialvoidPartialMethod();
}
publicpartialclassPartialClass// 사용자가 직접 작성하는 코드
{
publicvoidPartialMethod() {
Console.WriteLine("Partial method called.");
}
}
classProgram {
staticvoidMain(string[] args) {
PartialClass partial = new PartialClass();
partial.PartialMethod(); // "Partial method called." 가 출력된다
}
}
}
Partial 클래스는 클래스 선언을 분리 (주로 별도의 파일을 통해) 할 수 있는 기능을 제공한다. 일반적으로 자동 생성되는 코드를 수정하는 경우, 이후 코드가 다시 자동 생성될 때 사용자가 작성한 부분이 덮어써질 위험이 있으나, partial 클래스를 이용하면 이러한 문제를 해결할 수 있다. 또한 복수의 개발자들이 하나의 클래스나 메소드를 함께 개발하기에도 용이하다.
using System;
namespacePartialClassAndMethods {
publicpartialclassPartialClass {
publicvoidExampleMethod() {
Console.WriteLine("Method call from the first declaration.");
}
}
publicpartialclassPartialClass {
publicvoidAnotherExampleMethod() {
Console.WriteLine("Method call from the second declaration.");
}
}
classProgram {
staticvoidMain(string[] args) {
PartialClass partial = new PartialClass();
partial.ExampleMethod(); // "Method call from the first declaration." 가 출력된다partial.AnotherExampleMethod(); // "Method call from the second declaration." 가 출력된다
}
}
}
Section 44.1: C# 과 ASP.NET 에서 Unity 를 통한 Dependency Injection 사용하기
우선적으로, 사용자의 코드에서 Dependency Injection 을 사용하여야 할 이유는 무엇일까? 이는 사용자의 프로그램 내 특정 구성 요소 (component) 들을 다른 클래스들로부터 분리 (decouple) 하기 위함이다. 다음과 같은 AnimalController 클래스가 있다고 가정을 해보자:
public class AnimalController() {
private SantaAndHisReindeer _SantaAndHisReindeer = new SantaAndHisReindeer();
publicAnimalController() {
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.cs 와 UnityMvcActivator.cs
UnityConfig 의 RegisterTypes 메소드에서는, 사용자의 생성자에서 주입될 타입들을 확인할 수 있다.
namespaceVegan.WebUi.App_Start {
publicclassUnityConfig {
#region Unity Containerprivatestatic Lazy < IUnityContainer > container = new Lazy < IUnityContainer > (() => {
var container = new UnityContainer();
RegisterTypes(container);
return container;
});
///<summary>/// Gets the configured Unity container.///</summary>publicstatic 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>publicstaticvoidRegisterTypes(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")
]
namespaceVegan.WebUi.App_Start {
///<summary>Provides the bootstrapping for integrating Unity with ASP.NET MVC.</summary>publicstaticclassUnityWebActivator {
///<summary>Integrates Unity when the application starts.</summary>publicstaticvoidStart() {
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>publicstaticvoidShutdown() {
var container = UnityConfig.GetConfiguredContainer();
container.Dispose();
}
}
}
이제 우리는 Controller 를 SantAndHisReindeer 클래스로부터 분리할 수 있다.
public class AnimalController() {
privatereadonly SantaAndHisReindeer _SantaAndHisReindeer;
publicAnimalController(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;
namespaceVegan.WebUi {
publicclassMvcApplication: System.Web.HttpApplication {
protectedvoidApplication_Start() {
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
UnityWebActivator.Start();
}
}
}
Section 43.3: 스레드-안전한 지연된 Singleton (Double Checked Locking - 이중 검사 잠금 방식)
여기서 소개하고 있는 스레드-안전한 버전의 singleton 은 .NET 초기 버전들에서 필수적이었는데, 이는 당시 정적 (static) 초기화 과정이 스레드-안전함을 보장하지 않았기 때문이다. 이러한 아래와 같은 패턴은 구현 과정에서 실수를 유발할 가능성이 높으므로, 근래의 버전들에서는 정적 초기화 방식의 singleton 사용이 주로 선호된다.
publicsealedclassThreadSafeSingleton {
privatestaticvolatile ThreadSafeSingleton instance;
privatestaticobject lockObject = new Object();
privateThreadSafeSingleton() {}
publicstatic ThreadSafeSingleton Instance {
get {
if (instance == null) {
lock(lockObject) {
if (instance == null) {
instance = new ThreadSafeSingleton();
}
}
}
return instance;
}
}
}
눈여겨 볼 부분은 if (instance == null) 로써, 해당 확인 과정이 잠금 (lock) 을 획득하기 전에 한번, 직후에 한번, 총 두번에 걸쳐 등장하고 있다.
첫번째 등장하는 null 확인 과정이 없다고 하더라도 해당 구현은 여전히 스레드-안전할 것이나, 이는 해당 인스턴스를 요청할 때마다 매번 잠금을 획득하여야 하며, 그로 인해 성능 저하가 발생할 수 있다는것을 의미한다. 첫번째 null 확인 과정이 추가됨으로써, 꼭 필요하지 않은 경우에는 잠금을 획득하지 않도록 할 수 있다. 두번째 null 확인 과정은 잠금을 획득한 첫번째 스레드 하나만이 인스턴스 생성을 수행할 수 있음을 보장해 준다. 이후의 스레드들은 잠금을 획득한다고 하더라도 인스턴스가 이미 생성되었음을 확인할 수 있으므로 그 다음에 위치한 생성 코드를 수행하지 않을 것이다.