c# Reflection

C# 2019. 5. 6. 05:34

c#의 장점은 클래스 형태, 변수 타입등이 정확히 일치하지 않으면, 컴파일 자체가 일치하지 않으면, 명확히 오류를 발생 시키므로, javascript 등과 같이 데이터형 및 오타 등으로인한 오류가 숨어있을 가능성이 매우 적습니다. 하지만, 이를 위해 치르는 비용 또한 만만치 않은데, 약간만 틀려도 다른 클래스를 정의하거나, 추상 클래스, 상속등 코드가 커지는 단점 또한 발생합니다. 특히 완전히 다른 클래스 중 일부만 같지도 않고, 비슷하면, 모두 다른 클래스로, 만들어 관리해야 할지 모릅니다.

그렇게 된다면, 파일이 너무 크고, 관리해야할 포인트, 업데이트 및 유지 보수 비용에 막대하게 늘어 날 것 입니다. 이를 간단히 해결하는 방법으로, 어트리뷰트와 리플렉션을 사용하는 방법이 있습니다. 사실 c#의 매우 이른시기에 포함되었지만, 그렇게 흥미를 끄는 키워드는 아닌듯 합니다. 하지만, 이 기능을 잘 사용한다면, 분명 많은 노력과 시간을 줄일 수 있습니다.

저는 맥에서 비주얼 스튜디오 코드를 사용하지만, 윈도우에서 비주얼 스튜디오를 사용하시는 분들도 무리없이 따라 오실 수 있을 것 이라 봅니다.

 

[개발환경 및 환경구축]

개발환경 : mac, visual studio code
프로젝트 생성방법 : https://docs.microsoft.com/ko-kr/dotnet/core/tutorials/with-visual-studio-code . (C# 및 Visual Studio Code)

 

[공식문서]

Reflection : https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/concepts/reflection .
attribute에 저장된 정보 검색 : https://docs.microsoft.com/ko-kr/dotnet/standard/attributes/retrieving-information-stored-in-attributes .

 

1. 프로젝트 만들기.

프로젝트 생성 방법 문서를 참고 하도록 합니다.

프로젝트를 생성할 폴더를 파인더 (윈도우는 탐색기)를 통해 만들어 줍니다.

비주얼 스튜디오를 열어 Open Folder를 눌러 앞서 만든 폴더를 선택해 주도록 합니다.

주메뉴에서 보기 > 통합 터미널을 선택하여, 터미널을 열어 주도록 합니다.

콘솔에 dotnet new console 이라고 입력합니다. 그려면 아래와 같이 프로젝트가 초기화 되며, 처음 호출되는 메소드인 program.cs 파일도 자동으로 생성되어 집니다.

그냥 컴파일 후 시작하려면, 터미널에 dotnet run 이라고 입력하면됩니다.

오류를 찾기 위한 구성을 만들어 주려면 디버그 버튼과 화살표 버튼을 누른 후 .NET Core를 선택해 주도록 합니다.

그러면 구성이 추가되었습니다. 한 번 더, 디버그 > 삼각형 버튼을 눌러 주도록 합니다. 그러면 아래와 같이 DEBUG CONSOLE에 Hello World! 문자열이 찍히는 것을 확인할 수 있습니다.

 

 

2. Attribute 만들기.

Attribute를 만드는 것은 간단합니다. 클래스를 만들고, Attribute를 상속하기만 하면됩니다. 주의할 점은 클래스 명에, Attribute를 붙여 끝내야한다는 것입니다. 그렇게 하지 않아도 된다고 하는 데, 공식문서에서는 그렇게 하라고 합니다.

소스는 아래와 같습니다.

using System;

public class myAgeAttribute : Attribute
{


    // 초기화 시 입력받은 값들을 저장합니다.
    public int age = 0;


    // 생성자 입니다.
    public myAgeAttribute( int _age )
    {

        // 입력받은 값들을 저장합니다.
        age = _age;

    }

}

Attribute를 적용할 때는, Attribute클래스 명에서, Attribute를 제외하고 사용하면 됩니다. 저는 테스트를 위해 ex1, ex2라는 클래스를 만들었고, Attribute를 적용한 메소드가 적용하지 않은 메소드를 적절히 섞어 두었습니다.

*ex1

using System;

class ex1
{


    [myAge(16)]
    static public void myAge( int _age )
    {
        Console.WriteLine( string.Format("my Age : {0}", _age) );
    }

    static public void noAge( int _age )
    {
        Console.WriteLine( "호출 안되는 메소드" );
    }

}

*ex2

using System;

class ex2
{

    [myAge(18)]
    static public void yourAge( int _age )
    {
        Console.WriteLine( string.Format("Your Age : {0}", _age) );
    }

    [myAge(3)]
    static public void horseAge( int _age )
    {
        Console.WriteLine( string.Format("Horse Age : {0}", _age) );
    }

    static public void hideAge( int _age )
    {
        Console.WriteLine( "호출 안되는 메소드." );
    }


}

그리고, 테스트를 위해 program.cs 파일을 약간 고쳐 줍니다. 어셈블리는 모듈 (.dll) 단위가 프로그램 상에 로드될 때, 어셈블리를 형성하게 됩니다. c#에서는 주로 프로젝트 단위가 되는데, 스스로 만든 클래스는 어떤 프로젝트에 포함되어 있는지 우리 스스로가 알고 있습니다. 대게의 경우 자신이 만든 클래스는 자신이 생성한 하나 혹은 2개의 프로젝트에 들어 있을 것 이므로, 자신이 생성한 클래스 중 하나의 어셈블리를 가지고 오면, 자신이 만든 모든 어셈블리 일 것 입니다. (프로젝트가 2개인 경우라도 적절히 찾으실수 있을 것 입니다.)

아래 소스는 이런식으로 어셈블리를 찾아 와 모든 타입(클래스, 인터페이스등)에 myAgeAttribute가 붙어 있는 메소드를 찾아 Attribute에 설정된 값으로, 해당 메소드를 실행시켜 줍니다. 대게 실행하려면, 인스턴스화 되어야 하지만, 우리는  static 메소드만 만들었으므로, 인스턴스화 할 필요 없이 바로 실행해 주는 것이 가능했습니다.

using System;
using System.Reflection;

namespace reflection
{
    class Program
    {

        static void Main(string[] args)
        {


            // 어셈블리를 찾아 옵니다.
            Assembly myAssem = typeof(myAgeAttribute).Assembly;


            // 어셈블리에 포함된 모든 타입 (인터페이스, 클래스 등)을 찾아 옵니다.
            var types = myAssem.GetTypes();


            foreach( var _abc in types )
            {

                // 해당 타입의 메소드를 찾아 옵니다.
                var methods = _abc.GetMethods();

                foreach( var _curMethodinfor in methods )
                {

                    // myAgeAttribute가 있는 객체를 모두 찾아 옵니다.
                    var attributes = _curMethodinfor.GetCustomAttributes<myAgeAttribute>();
                    foreach( var nn in attributes )
                    {
                        // 해당 어트리 뷰트가 있는 경우만, 메소드를 실행해 주도록 합니다.
                        _curMethodinfor.Invoke( null, new object[]{ nn.age } );
                    }

                }

            }

        }

    }
}

실행해 보면 아래와 같은 결과를 만날 수 있습니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'C#' 카테고리의 다른 글

C# WebSocket Server  (6) 2019.05.01
Posted by 창업닉군
,