반응형
Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

개발꿈나무

[C# 교과서] 25. C# 활용(19) - 특성과 리플렉션 본문

C# 기초

[C# 교과서] 25. C# 활용(19) - 특성과 리플렉션

HYOKYE0NG 2022. 1. 25. 08:18
반응형
특성과 리플렉션

 

특성(attribute)은 프로그램에서 형식, 멤버, 다른 엔터티에 대한 추가 선언 정보를 저장한다.

 

특성

C#에서 특성은 데코레이트(decorator)와 애너테이션(annotation) 성격을 띈다. 간단히 말해 프로그램 코드에 설명을 추가로 붙이는 것이다.

  • 특성은 프로그램에 메타데이터(metdata)를 추가한다.
  • 데코레이터와 애너테이션 성격을 지닌다.
  • 꾸밈자(decorate, describe, declarative) 역할을 한다.
  • 여러 구성 요소에 추가 정보를 제공한다.

 

특성은 C# 구성 요소 앞에 대괄호([])로 표시한다.

[Obsolete]
public class OldClass{}
  • 닷넷에 내장된 특성: 닷넷에 내장된 특성으로 대괄호 기호를 멤버 앞에 붙여 사용
  • 사용자 지정 특성: 사용자가 새로운 특성을 직접 만드는 것으로 Attribute 클래스를 상속하는 클래스로 사용자 지정 특성을 생성

 

 

Obsolete 특성 사용하기

Obsolete 특성은 C# 9.0 이상의 버전에서만 사용 가능하다.

using System;

public class ObsoleteDemo
{
    static void Main()
    {
        void OldMember() => Console.WriteLine("Old Method");
        OldMember();

        [Obsolete] void NewMember() => Console.WriteLine("New Method");
        NewMember();
        
        [Obsolete("Using New Member Method")] //Obsolete 특성에 경고 메시지 지정 가능
        void NewMember2() => Console.WriteLine("New Method2");
        
        [Obsolete("Using New Member Method", true)] //두번째 매개변수를 true로 지정 -> 실행 오류
        void NewMethod3() => Console.WriteLine("New Method3");
    }
}

OldMember(), NewMember() 모두 실행에 에러는 나지 않지만,

비주얼 스튜디오에서는 Obsolete 특성이 적용된 메서드를 호출하면 컴파일러 단에서 경고 메시지가 표시된다.

Obsolete 특성에 경고 메시지를 지정할 수 있으며, 두번째 매개변수 값을 true로 지정하면 실행 에러가 난다.

 

 

[Conditional] 특성 사용하기

Conditional 특성을 사용하면 특정 기호(symbol)에 따라 실행 여부를 결정할 수 있다.

#define RELEASE //[2][1] 전처리기 지시문으로 RELEASE 기호 정의
using System;
using System.Diagnostics;

public class ConditionalDemo
{
    static void Main()
    {
        DebugMethod();
        ReleaseMethod();
    }

    [Conditional("DEBUG")] //[1] DEBUG 기호(심볼)을 가지는 경우에 실행
    static void DebugMethod() => Console.WriteLine("디버그 환경에서만 표시");

    //[2][2] RELEASE 기호가 있는 경우에 실행
    [Conditional("RELEASE")] static void ReleaseMethod() 
        => Console.WriteLine("릴리스 환경에서만 표시");
}

비주얼 스튜디오의 도구 모음은 Debug와 Release를 구분지을 수 있는 드롭다운 리스트를 제공하는데

이를 사용하여 프로그램에 DEBUG와 RELEASE 기호를 제공할 수 있다.

 

 

특성을 사용하여 메서드 호출 정보 얻기

using System.Runtime.CompilerServices;
using static System.Console;

class CallerInformation
{
    static void Main()
    {
        TraceMessage("실행");
    }

    public static void TraceMessage(string message,
            [CallerMemberName] string memberName = "",
            [CallerFilePath] string sourceFilePath = "",
            [CallerLineNumber] int sourceLineNumber = 0)
    {
        WriteLine("실행 내용: " + message);
        WriteLine("멤버 이름: " + memberName);
        WriteLine("소스 경로: " + sourceFilePath);
        WriteLine("실행 라인: " + sourceLineNumber);
    }
}


/*
실행 내용: 실행
멤버 이름: Main
소스 경로: C;\C#\CallerInformation\CallerInformation\CallerInformatin.cs
실행 라인: 8
*/

메서드의 매개변수 앞에서 특성을 사용하여 메서드를 호출한 호출자 정보를 얻을 수 있다.

 

 

사용자 지정 특성 만들기

클래스, 메서드 등에 대괄호를 붙여 사용할 수 있는 특성을 직접 원하는 이름으로 만들 수 있다.

using System;

// [1] Attribute 클래스를 상속하여 사용자 지정 특성 만들기 
public class SampleAttribute : Attribute
{
    public SampleAttribute() => Console.WriteLine("사용자 지정 특성 사용됨");
}

[Sample]
public class CustomAttributeTest { }

class AttributePractice
{
    static void Main()
    {
        // [2] CustomAttributeTest 클래스에 적용된 특성들 가져오기 
        Attribute.GetCustomAttributes(typeof(CustomAttributeTest));
    }
}

// 사용자 지정 특성 사용됨

[1] Attribute 클래스를 상속하여 SampleAttribute 이름의 특성 생성

사용자 지정 특성은 ~Attribute로 끝나고, 이를 줄여[Sample] 형태로 표현 가능

[2] CustomAttributeTest에 적용된 특성 목록을 가져오면서 SampleAttribute 클래스의 생성자를 호출하여 "사용자 지정 특성 사용됨" 문자열 출력

 

 

매개변수가 있는 사용자 지정 특성 만들기

using System;

// [1] AttributeUsage 특성을 사용하여 특성에 대한 제약 조건 등 설정
[AttributeUsage(
    AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)]
public class NickNameAttribute : Attribute
{
    public string Name { get; set; }
    public NickNameAttribute(string name) { Name = name; }
}

// [2] AllowMultiple에 의해서 여러 번 설정 가능
[NickName("길벗")]
[NickName("RedPlus")]
class NickNameAttributeTest
{
    static void Main() => ShowMetaData();

    static void ShowMetaData()
    {
        // 모든 커스텀 어트리뷰트 가져오기 
        Attribute[] attrs =
            Attribute.GetCustomAttributes(typeof(NickNameAttributeTest));
        foreach (var attr in attrs)
        {
            // [A] is 연산자를 사용하여 커스텀 어트리뷰트의 Name 속성 출력
            if (attr is NickNameAttribute)
            {
                NickNameAttribute ais = (NickNameAttribute)attr;
                Console.WriteLine("{0}", ais.Name);
            }
            // [B] as 연산자를 사용하여 커스텀 어트리뷰트의 Name 속성 출력
            NickNameAttribute aas = attr as NickNameAttribute;
            if (aas != null)
            {
                Console.WriteLine("{0}", aas.Name);
            }
        }
    }
}

 

 

리플렉션

리플렉션(reflection)은 동적으로 특정 어셈블리 또는 형식에 대한 메타데이터를 Type 개체로 반환하는 것을 의미한다. 리클렉션을 사용하면 특성 정보를 얻거나 동적으로 특정 형식을 로드하여 사용할 수 있다.

 

 

Type과 Assembly 클래스

using System;
using System.Reflection;

namespace ReflectionGetMembers
{
    class Test
    {
        public static void TestMethod() { }
    }

    class ReflectionGetMembers
    {
        static void Main()
        {
            // Test 클래스에 대한 Type 개체 가져오기 
            Type t = typeof(Test);

            // 원하는 멤버를 조건에 따라 가져오기 
            MemberInfo[] members =
                t.GetMembers(BindingFlags.Static | BindingFlags.Public);

            // 멤버 출력
            foreach (var member in members)
            {
                Console.WriteLine("{0}", member.Name); //TestMethod
            }
        }
    }
}

리플렉션을 사용하여 Test 클래스의 정적 멤버 리스트를 얻은 후 멤버 이름을 출력한다.

리플렉션을 사용하면 특정 클래스의 전체 멤버 리스트 또는 특정 조건에 맞는 멤버를 얻을 수 있다.

 

 

Type 클래스로 클래스의 멤버 호출하기

using System;
using System.Reflection;
namespace ReflectionGetMethod
{
    public class MemberClass
    {
        public string Name { get; set; } = "길벗출판사";
        public string GetName()
        {
            return Name + ", " + DateTime.Now.ToShortTimeString();
        }
    }

    class ReflectionGetMethod
    {
        static void Main()
        {
            //[1] 리플렉션 기능으로 특정 클래스의 멤버를 동적으로 호출(Invoke)
            MemberClass m = new MemberClass();
            Type t = m.GetType();

            //[a] 속성 읽어오기 및 속성 호출
            PropertyInfo pi = t.GetProperty("Name"); // Name 속성
            Console.WriteLine("속성 호출: {0}", pi.GetValue(m)); //길벗출판사

            //[b] 메서드 읽어오기 및 메서드 호출
            MethodInfo mi = t.GetMethod("GetName"); // GetName 메서드
            Console.WriteLine("메서드 호출: {0}", mi.Invoke(m, null)); //길벗출판사, 오전 1:29

            //[2] 참고: C# 4.0 이상에서는 dynamic 개체로 쉽게 멤버를 동적으로 호출
            dynamic d = new MemberClass(); // dynamic 키워드로 동적 개체 생성 
            Console.WriteLine("속성 호출: {0}", d.Name); // 속성 호출: 길벗출판사
            Console.WriteLine("메서드 호출: {0}", d.GetName()); // 메서드 호출: 길벗출판사, 오전 1:29   
        }
    }
}

 

 

특정 속성에 적용된 특성 읽어오기

using System;
using System.Reflection;

namespace ReflectionGetProperty
{
    class Person
    {
        [Obsolete] public string Name { get; set; }
    }

    class ReflectionGetProperty
    {
        static void Main()
        {
            // Name 속성의 정보 얻기
            PropertyInfo pi = typeof(Person).GetProperty("Name");

            // Name 속성에 적용된 특성 읽어오기
            object[] attributes = pi.GetCustomAttributes(false);
            foreach (var attr in attributes)
            {
                // 특성의 이름들 출력
                Console.WriteLine("{0}", attr.GetType().Name);
            }
        }
    }
}

//ObsolteAttribute

 

 

 

 

<Reference>

 

반응형
Comments