2016년 12월 6일 화요일

대리자(Delegate), 무명 메서드(Anonymous Method), 람다식(Lambda)

개인적으로 대충 정리한 내용입니다. 자세한 내용은 MSDN 링크를 참고하세요.

대리자(Delegate)

: https://msdn.microsoft.com/ko-kr/library/ms173171.aspx

대리자는 특정 매개 변수 목록 및 반환 형식이 있는 메서드에 대한 참조를 나타내는 형식입니다.
 
    public delegate int PerformCalculation(int x, int y);


  • 대리자는 C++의 함수 포인터와 유사하지만 형식이 안전합니다.
  • 대리자를 통해 메서드를 매개 변수로 전달할 수 있습니다.
  • 대리자를 사용하여 콜백 메서드를 정의할 수 있습니다.
  • 여러 대리자를 연결할 수 있습니다. 예를 들어 단일 이벤트에 대해 여러 메서드를 호출할 수 있습니다.
  • 메서드와 대리자 형식이 정확히 일치할 필요는 없습니다. 자세한 내용은 대리자의 가변성 사용을 참조하십시오.
  • C# 버전 2.0에는 별도로 정의된 메서드 대신 코드 블록을 매개 변수로 전달할 수 있도록 하는 무명 메서드라는 개념이 도입되었습니다. C# 3.0에는 인라인 코드 블록을 더 간단하게 작성할 수 있는 람다 식이 도입되었습니다. 특정 컨텍스트에서는 무명 메서드와 람다 식 모두 대리자 형식으로 컴파일됩니다. 이 두 기능을 익명 함수라고 합니다. 람다 식에 대한 자세한 내용은 익명 함수를 참조하십시오.
대리자 조합(멀티캐스트 대리자)
: https://msdn.microsoft.com/ko-kr/library/ms173175.aspx

이 예제에서는 멀티캐스트 대리자를 만드는 방법에 대해 설명합니다. 대리자 개체에는 + 연산자를 사용하여 대리자 인스턴스에 여러 개체를 할당할 수 있는 유용한 특성이 있습니다. 멀티캐스트 대리자에는 할당된 대리자 목록이 포함됩니다. 멀티캐스트 대리자를 호출하면 목록에 있는 대리자가 순서대로 호출됩니다. 같은 형식의 대리자만 조합할 수 있습니다.
- 연산자를 사용하면 멀티캐스트 대리자에서 구성 요소 대리자를 제거할 수 있습니다.

using System;

        // Define a custom delegate that has a string parameter and returns void.
        delegate void CustomDel(string s);

        class TestClass
        {
            // Define two methods that have the same signature as CustomDel.
            static void Hello(string s)
            {
                System.Console.WriteLine("  Hello, {0}!", s);
            }

            static void Goodbye(string s)
            {
                System.Console.WriteLine("  Goodbye, {0}!", s);
            }

            static void Main()
            {
                // Declare instances of the custom delegate.
                CustomDel hiDel, byeDel, multiDel, multiMinusHiDel;

                // In this example, you can omit the custom delegate if you 
                // want to and use Action<string> instead.
                //Action<string> hiDel, byeDel, multiDel, multiMinusHiDel;

                // Create the delegate object hiDel that references the
                // method Hello.
                hiDel = Hello;

                // Create the delegate object byeDel that references the
                // method Goodbye.
                byeDel = Goodbye;

                // The two delegates, hiDel and byeDel, are combined to 
                // form multiDel. 
                multiDel = hiDel + byeDel;

                // Remove hiDel from the multicast delegate, leaving byeDel,
                // which calls only the method Goodbye.
                multiMinusHiDel = multiDel - hiDel;

                Console.WriteLine("Invoking delegate hiDel:");
                hiDel("A");
                Console.WriteLine("Invoking delegate byeDel:");
                byeDel("B");
                Console.WriteLine("Invoking delegate multiDel:");
                multiDel("C");
                Console.WriteLine("Invoking delegate multiMinusHiDel:");
                multiMinusHiDel("D");
            }
        }
        /* Output:
        Invoking delegate hiDel:
          Hello, A!
        Invoking delegate byeDel:
          Goodbye, B!
        Invoking delegate multiDel:
          Hello, C!
          Goodbye, C!
        Invoking delegate multiMinusHiDel:
          Goodbye, D!
        */

대리자 사용
: https://msdn.microsoft.com/ko-kr/library/ms173172.aspx

C# 1.0 이상에서는 다음 예제와 같이 대리자를 선언할 수 있습니다.
    // Declare a delegate.
    delegate void Del(string str);

    // Declare a method with the same signature as the delegate.
    static void Notify(string name)
    {
        Console.WriteLine("Notification received for: {0}", name);
    }
    // Create an instance of the delegate.
    Del del1 = new Del(Notify); 

C# 2.0에서는 비슷한 방법을 통해 다음 예제에서와 같이 이전 예제의 선언을 작성할 수 있습니다.
        // C# 2.0 provides a simpler way to declare an instance of Del.
        Del del2 = Notify;
C# 2.0 이상에서는 다음 예제에서와 같이 무명 메서드를 사용하여 대리자를 선언하고 초기화할 수도 있습니다.
        // Instantiate Del by using an anonymous method.
        Del del3 = delegate(string name)
            { Console.WriteLine("Notification received for: {0}", name); };
C# 3.0 이상에서는 다음 예제에서와 같이 람다 식을 사용하여 대리자를 선언하고 인스턴스화할 수도 있습니다.
        // Instantiate Del by using a lambda expression.
        Del del4 = name =>  { Console.WriteLine("Notification received for: {0}", name); };


무명 메서드(Anonymous Method)

: https://msdn.microsoft.com/ko-kr/library/0yw3tz5k.aspx

2.0보다 이전 버전의 C#에서는 명명된 메서드를 사용하는 방법으로만 대리자를 선언할 수 있었습니다. C# 2.0에는 무명 메서드가 도입되었고 C# 3.0 이상에서는 무명 메서드 대신 람다 식을 사용하여 인라인 코드를 작성하는 방법이 더 선호됩니다.
무명 메서드에는 람다 식에 없는 기능이 한 가지 있습니다. 무명 메서드를 사용하면 매개 변수 목록을 생략할 수 있으며, 이는 무명 메서드가 여러 시그니처를 가진 대리자로 변환될 수 있음을 의미합니다. 람다 식을 사용해서는 이렇게 할 수 없습니다. 람다 식에 대한 자세한 내용은 람다 식을 참조하십시오.

        // Create a handler for a click event.
        button1.Click += delegate(System.Object o, System.EventArgs e)
                           { System.Windows.Forms.MessageBox.Show("Click!"); };
    // Create a delegate.
    delegate void Del(int x);

    // Instantiate the delegate using an anonymous method.
    Del d = delegate(int k) { /* ... */ };
무명 메서드를 사용하면 별도의 메서드를 만들 필요가 없으므로 대리자를 인스턴스화하는 데 따르는 코딩 오버헤드를 줄일 수 있습니다.

무명 메서드의 매개 변수 범위는 무명 메서드 블록입니다.
무명 메서드 블록 안에서 블록 밖을 대상으로 goto, break 또는 continue와 같은 점프 문을 사용하면 오류가 발생합니다. 무명 메서드 블록 밖에서 블록 안을 대상으로 goto, break 또는 continue와 같은 점프 문을 사용해도 오류가 발생합니다.
지역 변수 및 매개 변수의 범위에 무명 메서드 선언이 포함되는 경우 이러한 변수를 무명 메서드의 외부 변수라고 합니다. 예를 들어, 다음 코드 단편에서 n은 외부 변수입니다.
        int n = 0;
        Del d = delegate() { System.Console.WriteLine("Copy #:{0}", ++n); };

람다식(Lambda)

: https://msdn.microsoft.com/ko-kr/library/bb397687.aspx

람다 식은 대리자 또는 식 트리 형식을 만드는 데 사용할 수 있는 익명 함수입니다. 람다 식을 사용하여 인수로 전달되거나 함수 호출 값으로 반환되는 로컬 함수를 쓸 수 있습니다. 람다 식은 LINQ 쿼리 식을 작성하는 데 특히 유용합니다.

람다 식을 만들려면 람다 연산자 => 왼쪽에 입력 매개 변수를 지정하고(있는 경우) 다른 쪽에 식이나 문 블록을 삽입합니다. 예를 들어 람다 식 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  
}  

식 트리 형식을 만들려면
using System.Linq.Expressions;  
  
namespace ConsoleApplication1  
{  
    class Program  
    {  
        static void Main(string[] args)  
        {  
            Expression<del> myET = x => x * x;  
        }  
    }  
}  

=> 연산자는 할당(=)과 우선 순위가 같고 오른쪽 결합성이 있습니다(연산자 문서의 "결합성" 단원 참조).
 무명 메서드에 적용되는 모든 제한은 람다 식에도 적용됩니다. 자세한 내용은 무명 메서드을 참조하세요.

Lambda는 내용이 많아서 생략..

2016년 12월 5일 월요일

Singleton pattern in C#

사실 singleton class를 별로 좋아하진 않지만
GUI layer에서 사용해야할 기능 제공을 위해 Singleton을 종종 사용한다.

주로 Application 개발 시 implementation 부분을 별도로 만들 때
GUI 부분에서 필요한 API들을 제공하기 위해 유용하고
API 구현을 위해 class들의 feature를 조합하는 mediator 역할을 하고
implementation의 class들의 instance들도 가지고 있도록 하기 위한 목적으로
singleton class을 사용한다.

구현 방법에 따라서 사실 메모리 사용 측면에서는 좀 별로 일 수 있다.
그리고 singleton pattern을 될 수 있으면 static method로 대체하려고 하고 주로 utility 성격의 class들은 그렇게 사용하고 있긴함.

C++에서는 메이어스아저씨의 singleton class를 주로 사용하였는데
역시나 C#에서도 비슷한 것이 있어서 이를 사용하였음.

여기가 가장 잘 정리된 곳 같음.
http://csharpindepth.com/Articles/General/Singleton.aspx

잠깐 케이스별로 가져다 보자면

First version - not thread-safe

// Bad code! Do not use!
public sealed class Singleton
{
    private static Singleton instance=null;

    private Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            if (instance==null)
            {
                instance = new Singleton();
            }
            return instance;
        }
    }
}

thread safe하지 않아 singleton pattern에 맞지 않음.

Second version - simple thread-safety

public sealed class Singleton
{
    private static Singleton instance = null;
    private static readonly object padlock = new object();

    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            lock (padlock)
            {
                if (instance == null)
                {
                    instance = new Singleton();
                }
                return instance;
            }
        }
    }
}

lock을 사용하여 thread safe하도록 수정한 버전
이것에 lock 전에 instance 확인을 하는 double checking version이 있으나 volatile을 사용하지 않는 한 동작에 문제가 있을 수 있고 또한 performance 상에서도 두번째 버전과 별 차이가 없어 비추인듯 함.

Fourth version - not quite as lazy, but thread-safe without using locks

public sealed class Singleton
{
    private static readonly Singleton instance = new Singleton();

    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static Singleton()
    {
    }

    private Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            return instance;
        }
    }
}

선호하는 모델인데 C#에서 static constructor는 class instance가 생성되거나 static member가 참조될 때 한번만 호출 되어 위 코드의 instance는 어느정도? lazy함이 보장된다고 하지만 C# compiler 특성 상 lazy instantiation이 보장되지 않을 수 있다고 함.

Fifth version - fully lazy instantiation

public sealed class Singleton
{
    private Singleton()
    {
    }

    public static Singleton Instance { get { return Nested.instance; } }
      
    private class Nested
    {
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Nested()
        {
        }

        internal static readonly Singleton instance = new Singleton();
    }
}

마지막으로는 nested class에 instnace를 선언하고 이를 enclosing class에서 참조하는 방법이다. 4번째 버전에 비해 fully lazy 하다는데 이유를 잘 모르겠다. 단지 static constructor를 사용하는 것 밖에 없는 것 같은데.. 내가 공부 좀 더해야할 듯..
nested class를 사용하고 inernal 선언을 통해서 enclosing class외에서는 접근 조차 못하도록 한 것이 좀 다르긴한데 4번째 버전과 큰 차이는 잘 모르겠음.


Sixth version - using .NET 4's Lazy<T> type

If you're using .NET 4 (or higher), you can use the System.Lazy<T> type to make the laziness really simple. All you need to do is pass a delegate to the constructor which calls the Singleton constructor - which is done most easily with a lambda expression.
public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy =
        new Lazy<Singleton>(() => new Singleton());
  
    public static Singleton Instance { get { return lazy.Value; } }

    private Singleton()
    {
    }
}

그리고 간단하게 .NET 4의 System.Lazy<T>를 사용하여 간단하게 구현할 수 있다고도 함.
뭐 나야 이건 아직 안써봤으니 뭐라 말하기가 ㅎㅎㅎ

2016년 12월 2일 금요일

Tizen 기반 기기(Gear, Z)의 Tizen platform version 확인하는 방법


Tizen 기기에 올릴 app을 만들려면
Tizen Sutdio에서 Tizen 기기에 맞는 Tizen Platform을 선택해야 하는데 모호한 경우가 많음.
이때는 Tizen 기기의 아래 파일을 보면 되는데
가지고 있는 Gear S2를 확인해 보면 /etc/config/model-config.xml 의 값을 확인하는 것이 맞겠다.

Tizen Platform version을 확인할 수 있는 파일들

- cat /etc/tizen-release
- cat /etc/config/model-config.xml

근데 확인해 보면
tizen-release 파일과 model-config 파일의 내용이 좀 다른 것을 알 수 있는데
이유는 잘 모르겠고 결과적으로 model-config의 Tizen 2.3.1이 맞지 않는가 생각 됨.

$ sdb connect / $ sdb connect <무선 연결이라면 기기 IP adress>
$ sdb shell
sh-3.2$ cat /etc/tizen-release
Tizen release 2.3.0 (Magnolia)

sh-3.2$ cat /etc/config/model-config.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<model-config version="2.3.1" model="SM-R732">
        <platform>
                <key name="tizen.org/system/model_name" type="string">SM-R732</key>
                <key name="tizen.org/system/platform.name" type="string">Tizen</key>
                <key name="tizen.org/feature/platform.version" type="string">2.3.1.7</key>
                <key name="tizen.org/feature/platform.version.name" type="string">Tizen</key>
                <key name="tizen.org/system/platform.processor" type="string">Exynos 3250</key>
                <key name="tizen.org/system/platform.communication_processor" type="string">none</key>


참고로 model-config의 값들은 아래 API를 사용하여 획득할 수 있음