2008. 10. 3. 17:21

[펌] 어트리뷰트(Attribute)


 어트리뷰트

Attribute는 클래스안에 메타정보를 포함 시킬수 있는 기술로서 클래스, 데이터 구조, 열거자 그리고 어셈블리와 같은 프로그래밍적 요소들의 실행시 행동에 대한 정보를 기술 할 수 있다.

다음과 같은 프로그래밍 요소에 대해 어트리뷰트를 사용 할 수 있다.
어셈블리, 모듈, 클래스, 구조체, 열거형변수, 생성자, 메소드, 프로퍼티, 필드, 이벤트, 인터페이스, 파라미터, 반환값, 델리게이트

어트리뷰트 역시 클래스의 일종이다.

어트리뷰트의 문법은 다음과 같다.
[attribute(positional_parameter, name_parameter = value, …) ]
파라미터에는 두 종류가 있는데 하나는 위치지정 파라미터로 반드시 들어와야 한다. 명명파라미터(name_parameter)는 꼭 필요하지는 않은 구조를 가지면 어트리뷰트 안에 추가적인 정보를 넣을 때 이용한다.

예를들면

[AdditionalInfo(“2004-01-01”)]
public class Test { ..}

원하는 프로그래밍 요소 위에 Attribute를 써주면 그곳에 Attribute가 배정되는 것이다.

1.Conditional Attribute
C# 디버깅을 지원하기 위해 Conditional Attribute를 사용 할 수 있다. Conditional Attribute는 사용자가 정의 한 값에 의해서 해당 메소드를 실행 하도록 한다.

#define SOUNDCARD // 이 부분을 지운 후 실행 해 보자
using System;
using System.Diagnostics;

class Test
{
        //Conditional Attribute인 경우 지정된 위치지정 파라미터가 정의 되어
        //있을때 실행이 되는 것이다.
        [Conditional ("SOUNDCARD")]
        static void print()
        {
                Console.WriteLine("도레미...");
        }

        static void Main()
        {
                print();
        }
}

위 예제에서 #define SOUNDCARD 부분을 지우지 않은 상태에서는 도레미... 가 정상적으로 출력 되지만 지운 상태에서는 출력 되는 않는다.

Conditional Attribute는 클래스나 구조체 안에 있는 메소드 에서만 사용 할 수 있다. 또 그 메소드는 void 형 이어야 한다. 다음의 예문처럼 메소드에 대해 여러 Conditional Attribute를 붙이게 되면 그 중 하나만 위치지정 파라미터가 정의 되어 있어도 그 메소드를 실행 시킨다.

만약 두개의 Conditional Attribute를 모두 만족해야 메소드를 실행 할려면 아래와 같은 방법을 이용하면 된다.

[Conditional(“MODEM”), Conditional(“ADSL”)]
static void print() { .. }

#define SOUNDCARD
#define SPEAKER

using System;
using System.Diagnostics;

class Test
{
        
        [Conditional ("SOUNDCARD")]
        static void isSound()
        {
                isSpeaker();
        }

        [Conditional ("SPEAKER")]
        static void isSpeaker()
        {
                Console.WriteLine("음악을 들을 수 있습니다...");
        }

        static void Main()
        {
                isSound();
        }
}

[결과]
음악을 들을 수 있습니다...

2.DllImport 어트리뷰트
DllImport Attribute는 C#안에서 Unmanaged Code를 사용 할 수 있게 한다. Unmanaged Code란 닷넷 환경밖에서 개발된 코드를 가리킨다. DllImport Attribute는 System.Runtime.InteropServices 네임스페이스 안에 정의 되어 있다. 사용방법은 위치지정 파라미터로 사용 할 DLL 파일을 인자로 넘겨 주면 된다.

using System;
using System.Runtime.InteropServices;

class Test
{
        //User32.dll 파일안의 MessageBox 함수를 불러와서 사용하는 예이다. DllImport Attribute를
        //이용하여 사용할 코드가 포함되어 있는 DLL을 넘겨주고 extern 키워드를 통해 사용하려고 하는
        //메소드가 외부에 있음을 알린다. 이렇게 하면 닷넷 환경 밖에서개발된 코드들도 C#안에서 쓸수 있다.
        [DllImport("User32.Dll")]
        public static extern int MessageBox(int h, string m, string c, int type);

        static void Main()
        {
                MessageBox(0, "Hello!", "In C#", 0);  //다이얼로그 창의 타이블이 "In C#" 이며 내용은 "Hello"
        }
}


3.Custom Attribute의 정의

-어트리뷰트의 범위 지정

앞에서 Conditional Attribte는 메소드에만 붙일 수 있다고 했다. 그렇다면 사용자가 만든 어트리뷰트에 대해 어떤 곳에서만 붙일 수 있게 하는 방법은 없을까? AttributeUsage를 이용해서 사용자가 정의한 데이터 형에만 어트리뷰트를 붙일 수 있다.

[AttributeUsage(AttrbuteTargets.Method)]
public class AdditionalInfo { … }

AttributeUsage 역시 Attrbute 이며 사용자가 정의할 어트리뷰트 앞에 사용됨으로서 범위를 지정 할 수 있다. Method 이외에 AttrbuteTargets.Class, AttrbuteTargets.Delegate, AttrbuteTargets.Interface, AttrbuteTargets.Propert, AttrbuteTargets.Construct 등등을 사용 할 수 있다.

만약 여러 개의 데이터 형에 붙일려면 ‘|’를 이용하면 된다.

[AttributeUsage(AttrbuteTargets.Method | AttrbuteTargets.Delegate)]
public class AdditionalInfo { … }

또한 모든 데이터 형에 붙이는 것을 가능하게 할려면 AttrbuteTargets.All 이라고 해 주면 된다.

-어트리뷰트 클래스 선언

간단히 클래스에 대한 제작자, 업데이트 날짜, 최신 버전을 다운 받을 수 있는 곳등의 정보를 담을 수 있는 어트리뷰트를 만들어 보도록 하겠다. 다른 클래스를 만드는 것처럼 비슷하게 클래스를 작성 하면 어트리뷰트가 구현된다. 모든 어트리뷰트는 System.Attribut로부터 상속 받는다. 즉 보통 클래스를 선언하는 똑 같은 방법으로 선언하고 다만 System.Attriute로부터 상속을 받으면 그 클래스가 어트리뷰트가 되는 것이다. 일반 클래스와 구분하기 위해 접미사로 ‘Attribute’를 붙일 것을 권고 한다.

어트리뷰트도 클래스 이므로 생성자가 존재 한다. 다만 어트리뷰트는 하나의 생성자만 가질 수 있다. 즉 생성자 오버로딩이 불가능 하다. 그럼 추가로 받아야 하는 정보들은 어떻게 처리 할까? 어트리뷰트에서는 생성자에서 꼭 집어 넣어야 하는 데이터는 위치지정 파라미터로 받고 부가적인 데이터는 명명 파라미터를 통해 받아 들임으로서 문제를 해결 한다.

[예제]
using System;

[AttributeUsage(AttributeTargets.Class)]
public class AdditionalInfoAttribute: Attribute
{
        //생성자에 있는 두개의 인자는 위치지정 파라미터 이다.
        //즉 위치지정 파라미터는 클래스에 어트리뷰트를 붙일때 반드시 넘겨 줘야 한다.
        //항상 생성자에서 값을 넘겨 주게 되어 있으므로 name, update인 경우 Property에서
        //set이 없다.
        public AdditionalInfoAttribute(string name, string update)
        {
                this.name = name;
                this.update = update;
        }

        public string Name
        {
                get { return name; }
        }

        public string Update
        {
                get { return update; }
        }

        public string Download
        {
                set { download = value; }
                get { return download; }
        }

        string name;
        string update;
        string download;
}

// 아래의 경우처럼 위치지정 파라미터는 생성자에 값을 그대로 전달하지만 명명 파라미
// 터는 파라미터 이름에 값을 대입하여 생성자에 넘겨야 한다.
[AdditionalInfo(“jclee”,’2004-01-01”, Download = http://www.oraclejava.co.kr)]
class Test { … }


클래스에 어트리뷰트를 붙였다면 반대로 클래스로부터 어떤 어트리뷰트들이 붙어 있는지 아는 하는 방법도 있어야 할 것이다. 바로 전 예제에서 Test라는 클래스에 어트리뷰트를 붙였는데 반대로 Test라는 클래스로부터 어트리뷰트 정보를 알아내는 방법을 예제를 통해 보도록 하자.

public class AttrInfo
{
        public static void Main()
        {
                Type type = typeof(Test);

                foreach(Attribute attr in type.GetCustomAttributes(true))
                {
                        AdditionalInfoAttribute info = attr as AdditionalInfoAttribute;

                        if (info != null)
                        {
                                Console.WriteLine("Name={0}, Update={1}, DownLoad={2}", info.Name, info.Update, info.Download);
                        }
                }
        }
}

4.사용자정의 어트리뷰트의 처리 과정

-어트리뷰트 클래스를 찾는다.
-어트리뷰트의 범위를 체크 한다.
-어트리뷰트의 생성자를 체크 한다.
-객체의 인스턴스를 생성 한다.
-명명 파라미터들을 체크 한다.
-명명 파라미터 값으로 필드나 프로퍼티의 값을 설정 한다.
-어트리뷰트 클래스의 현재 상태를 저장한다.

5.Multiple Attribute의 사용
한 개의 프로그래밍 요소에 한 개 이상의 어트리뷰트를 붙일 수 있다.

public class Csharp {
        [Conditional(“CLR”)]
        [FAQ(http://www.help.com/chapter1)]
public void chapter1() { … }
}

한 개의 프로그래밍 요소에 여러 개의 어트리뷰트를 붙일 수 있지만 앞에서 만든 AdditionalInfoAttribute를 아래 처럼 중복해서 사용하면 오류가 발생 한다.

[AdditionalInfoAttribute(“jclee”,”2004-01-01”, Download=http://www.oraclejava.co.kr)]
[AdditionalInfoAttribute(“tatata”,”2004-02-01”, Download=http://www.oraclejava.co.kr)]
class OracleJava { … }

어트리뷰트는 기본적으로 한 어트리뷰트에 한 개의 인스턴스만 사용 가능하지만 AllowMultiple을 true로 지정하면 위에서와 같이 같은 어트리뷰트를 여러 개 사용 가능하다.


6.어트리뷰트로부터 값 얻어오기
-클래스 메타 데이터
여기서는 Reflection을 이용하여 값을 얻어오는 것을 살펴 보도록 하자. 닷넷 프레임 웍에서는 메타데이터를 검사 할 수 있는 클래스들이 포함된 System.Reflection 이라는 네임스페이스를 제공 하고 있다. 이 Reflection 네임스페이스 안에 있는 MemberInfo 라는 클래스는 클래스의 어트리뷰트를 알아볼 때 유용하게 사용 될 수 있다. System.Type 객체의 GetMembers 라는 메소드를 적절히 구사하여 클래스 메타데이터 를 조사 할 수 있다.

System.Reflection.MemberInfo[] members;
Members = typeof(MyClass).GetMembers();


-어트리뷰트의 정보 얻기
MemberInfo 객체는 GetCustomAttributes 라는 메소드를 가지고 있다. 이 메소드는 모든 어트리뷰트의 정보를 배열로 알아 낼 수 있다. 이렇게 알아낸 정보를 반복문을 이용하여  어트리뷰트를 조사 할 수 있다.

 

출처 :  http://cafe.naver.com/itclean.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=1098