ITEM 20 : 변경불가능한 아토믹 값타입을 선호해라

|

ITEM 20 :  Prefer Immutable Atomic Value Type

           (변경불가능한 아토믹 값타입을 선호해라)



■ 요약 및 장점


+ 다른 곳에서 해당 type의 내부의 상태를 변경하기 못하게 하려면 immutable한 type을 선언해줘야 한다.

  그것이 보장되지 않으면 내부의 상태를 변경하지 못하게 검사하는 비용이 많이 들어가게 된다.

  여기에서는 초기에 생성된 후에는 변경되지 않게 보장하는 방법에 대해서 설명한다.


1. Immutable type은 생성된후에 변경하지 못하는 타입이다. 즉 상수(constant)이다.


2. Immutable type은 hash-based collection에서 더 잘 동작한다.


3. atomic type은 하나의 엔터티로 구성되어진 것을 말한다. 즉 더 쪼갤수 없는 단위를 말한다.


4. 아래의 예제는 변경을 하게 되면 유효하지 않는 데이터가 일시적으로 유지된다. 
  별 문제 없어 보이지만 만약에 멀티쓰레드 환경에서는 문제가 될수 있다. 
  즉, 한쪽에서 바꾸는 중간에 다른쪽에서 읽어 갈수 있기 때문이다.

// Mutable Address structure.
public struct Address
{
        private string state;
        private int zipCode;

        // Rely on the default system-generated
        // constructor.
        public string Line1
        {
            get;
            set;
        }

        public string Line2
        {
            get;
            set;
        }

        public string City
        {
        get;
        set;
        }

        public string State
        {
            get { return state; }
            set
            {
                ValidateState(value);
                state = value;
            }
        }
        public int ZipCode
        {
            get { return zipCode; }
            set
            {
                ValidateZip(value);
                zipCode = value;
            }
        }

    // other details omitted.
}

// Example usage:
Address a1 = new Address();
a1.Line1 = "111 S. Main";
a1.City = "Anytown";
a1.State = "IL";
a1.ZipCode = 61111;
// Modify:
a1.City = "Ann Arbor"; // Zip, State invalid now.
a1.ZipCode = 48103; // State still invalid now.
a1.State = "MI"; // Now fine.

5. 아래의 예제는
 immutable한 구조체를 선언한것이다. 위는 변경이 불가능하지만 엄격하게보면 내부적으로는
setter가 있기때문에 변경이 가능하긴하다. 다만 외부에서는 변경이 불가능하다.

 public struct Address2
    {
        // remaining details elided
        public string Line1
        {
            get;
            private set;
        }
        public string Line2
        {
            get;
            private set;
        }
        public string City
        {
            get;

            private set;
        }
        public string State
        {
            get;
            private set;
        }
        public int ZipCode
        {
            get;
            private set;
        }
    }

6. 완벽하게 immutable하게 하려면 implicit properties를 explicit properties로 변경하고 backing field(해당 프로퍼티에 
  해당하는 멤버변수)를 readonly로 한정해줘야한다.

public string Line1
 {
    get { return Line1; }
 }

 private readonly string line1;

7. immutable type을 선언할때 주의할점이 있다. 프로퍼티가 외부의 참조타입을 참조하면 외부에서 수정될 가능성이 존재한다.

public struct PhoneList
    {
        private readonly Phone[] phones;
        public PhoneList(Phone[] ph)
        {
            phones = ph;
        }
        public IEnumerable<Phone> Phones
        {
            get
            {
                return phones;
            }
        }
    }

    Phone[] phones = new Phone[10];
    // initialize phones
    PhoneList pl = new PhoneList(phones);
    // Modify the phone list:
    // also modifies the internals of the (supposedly)
    // immutable object.
    phones[5] = Phone.GeneratePhoneNumber();

8.이때는 생성자에서 해당 참조값에 대해서 복사본은 생성한 다음에 초기화 한다.

 public PhoneList2(Phone[] ph)
   {
     phones = new Phone[ph.Length];
     // Copies values because Phone is a value type.
     ph.CopyTo(phones, 0);
   }

■ Terms



■ 단어


Immutable:불변의,변경할 수 없는

validate:입증하다,인증하다,승인하다.

export:내보내다,수출하다.

invariant:변함없는,불변의

harmless:무해한.

fragment:조각,파편

specify:명시하다.

interim:중간의, 잠정적인

strictly:엄격하게

pitfall:함정,위험




And

[C#]익명메서드와 람다식

|

2.0보다 이전 버전의 C#에서는 명명된 메서드를 사용하는 방법으로만 대리자를 선언할 수 있었습니다.

C# 2.0에는 무명 메서드가 도입되었고 C# 3.0 이상에서는 무명 메서드 대신 람다 식을 사용하여 인라인 코드를

작성하는 방법이 더 좋습니다.


1.익명메서드


// Create a delegate.
 delegate void Del(int x);

 // Instantiate the delegate using an anonymous method.
 Del d = delegate(int k) { /* ... */ };


무명 메서드를 사용하면 별도의 메서드를 만들 필요가 없으므로 대리자를 인스턴스화하는 데 따르는

코딩 오버헤드를 줄일 수 있습니다.


 예를 들어, 메서드를 생성할때 불필요한 오버헤드가 발생된다면 대리자(delegate) 대신에 코드블록을 사용

하는 것이 좋습니다. 또 다른 예는 새로운 스레드를 시작할 때입니다.


다음 클래스는 스레드를 만들고 스레드에서 실행할 코드도 포함하므로 대리자를 위한 추가 메서드를 

만들 필요가 없습니다.


void StartThread()
{
    System.Threading.Thread t1 = new System.Threading.Thread
      (delegate()
            {
                System.Console.Write("Hello, ");
                System.Console.WriteLine("World!");
            });
    t1.Start();
}


2.람다식


람다 식을 만들려면 람다 연산자 => 왼쪽에 입력 매개 변수를 지정하고(있는 경우) 다른 쪽에 식이나 문 블록을 넣습니다.

예를 들어, 람다 식 x => x * x는 이름이 x인 매개 변수를 지정하고 x 제곱 값을 반환합니다. 


다음 예제와 같이 대리자 형식에 이 식을 할당할 수도 있습니다.


delegate int del(int i);
static void Main(string[] args)
{
    del myDelegate = x => x * x;
    int j = myDelegate(5); //j = 25
}


람다식은 두가지의 방법이 존재합니다.


2-1 식 람다(expression)


 => 연산자의 오른쪽에 식이 있는 람다 식을 식 람다라고 합니다.


괄호는 람다 식에 입력 매개 변수가 하나뿐인 경우에만 생략할 수 있고 그렇지 않으면 생략할 수 없습니다.

둘 이상의 입력 매개 변수는 다음과 같이 괄호로 묶고 쉼표로 구분해야 합니다.

(x, y) => x == y


컴파일러에서 입력 형식을 유추할 수 없는 경우도 있습니다. 이와 같은 경우에는 다음 예제와 같이 형식을 

명시적으로 지정할 수 있습니다.

(int x, string s) => s.Length > x



2-2 문 람다(statement)


문 람다는 다음과 같이 중괄호 안에 문을 지정한다는 점을 제외하면 식 람다와 비슷합니다.

문 람다의 본문에 지정할 수 있는 문의 개수에는 제한이 없지만 일반적으로 2-3개 정도만 지정합니다.


delegate void TestDelegate(string s);
…
TestDelegate myDel = n => { string s = n + " " + "World"; Console.WriteLine(s); };
myDel("Hello");


And

[C#] Func<T, TResult> Delegate (델리게이트 캡슐화)

|


델리게이트를 캡슐화 해서 명시적으로 델리게이트를 선언하지 않고도 이 제네릭을 이용하면 간편하게 사용이 가능하다. 

Func<T, TResult>는 내부적으로 델리게이트를 선언해서 사용하지만 사용자에게는 노출하지 않습니다.

따라서 사용자는 델리게이트를 몰라도 사용이 가능합니다.


T는 입력 파라미터, TResult는 결과 값이다.


1. 기존의 방법


delegate string ConvertMethod(string inString);
 
public class DelegateExample
{
   public static void Main()
   {
      // Instantiate delegate to reference UppercaseString method
      ConvertMethod convertMeth = UppercaseString;
      string name = "Dakota";
      // Use delegate instance to call UppercaseString method
      Console.WriteLine(convertMeth(name));
   }
 
   private static string UppercaseString(string inputString)
   {
      return inputString.ToUpper();
   }
}


2. Func(T,TResult) 대리자 사용시


public class GenericFunc
{
   public static void Main()
   {
      // Instantiate delegate to reference UppercaseString method
      Func<string, string> convertMethod = UppercaseString;
      string name = "Dakota";
      // Use delegate instance to call UppercaseString method
      Console.WriteLine(convertMethod(name));
   }
 
   private static string UppercaseString(string inputString)
   {
      return inputString.ToUpper();
   }
}


3. 할당은 무명메서드나 람다식을 이용도 가능하다.


public class Anonymous
{
   public static void Main()
   {
      Func<string, string> convert = delegate(string s)
         { return s.ToUpper();}; 
 
      string name = "Dakota";
      Console.WriteLine(convert(name));   
   }
}
 
 
public class LambdaExpression
{
   public static void Main()
   {
      Func<string, string> convert = s => s.ToUpper();
 
      string name = "Dakota";
      Console.WriteLine(convert(name));   
   }
}


And