C# 애트리뷰트

2025. 2. 25. 23:30개발/C#

이 글은 박상현님의 '이것이 C#이다 개정판'을 참고하여 공부한 내용입니다.


 

저희는 코드로 담을 수 없는 부가정보를 주석을 통해 표현합니다. 사람의 경우에는 이 주석을 통해 코드의 자세한 사용법을 익히거나 동작을 예상할 수 있을 겁니다. 하지만 주석은 컴파일 타임에 무시되어 런타임에서는 확인할 수 없습니다. 그렇다면 컴파일타임 혹은 런타임중에 부가정보를 전달할 수 있는 방법은 없을까요?

 

애트리뷰트, Attribute

클래스, 메소드, 구조체, 프로퍼티 등과 같은 요소에 추가적인 메타데이터를 제공하는 기능입니다. 애트리뷰트를 사용하면 컴파일타임 혹은 런타임에 특정동작을 수행할 수 있습니다. 대표적인 애트리뷰트로는 Obsolete가 있습니다.

using System;

class Program
{
    [Obsolete("Don't use OldMethod. Use NewMethod")]
    public static void OldMethod()
    {
        Console.WriteLine("Old Method");
    }

    public static void NewMethod()
    {
        Console.WriteLine("New Method");
    }

    static void Main()
    {
        OldMethod();
        NewMethod();
    }
}

 

CS0618 'Program.OldMethod()' is obsolete: 'Don't use OldMethod. Use NewMethod'

 

Old Method
New Method

 

Obsolete는 컴파일러에게 정보를 전달하여 경고를 발생시킵니다. 주로 해당 클래스와 메서드가 더 이상 사용되지 않도록 표시할 때 사용합니다. 그래서 위 코드를 컴파일 하면 경고가 발생합니다.

 

[ Attribute Name( Attribute Parameter ) ]

 

애트리뷰트는 대괄호 안에 애트리뷰트의 이름을 넣어, 적용할 요소 위에 선언하여 사용합니다.

 

호출자 정보 추적, Caller Information Attributes

C#에는 현재 메서드를 호출한 메서드 혹은 프로퍼티의 정보를 추적할 수 있는 애트리뷰트가 존재합니다. 이 애트리뷰트를 사용하면 로그 추적 및 디버깅을 원활하게 할 수 있습니다.

애트리뷰트 설명
CallerMemberName 현재 메서드를 호출한 메서드 혹은 프로퍼티의 이름
CallerFilePath 현재 메서드를 호출한 소스 파일 경로
CallerLineNumber 현재 메서드를 호출한 소스 파일 내의 행 번호

 

아래에는 호출자 정보 추적 애트리뷰트를 사용한 예시 입니다.

using System;
using System.Runtime.CompilerServices;

class MyClass
{
    public static void Print(string message,
        [CallerFilePath] string file = "",
        [CallerLineNumber] int line = 0,
        [CallerMemberName] string member = "")
    {
        Console.WriteLine($"{file}:{line}:{member}:{message} ");
    }
}

class Program
{
    static void Main()
    {
        MyClass.Print("Hello World!");
    }
}

 

C:\Users\leebo\source\repos\TestApp\TestApp\Program.cs:19:Main:Hello World!

 

호출자 정보 추적 애트리뷰트는 선택적 매개변수로 사용되며, 컴파일러가 호출자 정보를 자동으로 값을 설정해 주기 때문에 호출코드에서 명시적으로 넘겨줄 필요는 없습니다.

 

사용자 지정 애트리뷰트

애트리뷰트는 사용자가 새롭게 정의하여 임의 정보를 해당 객체에 적용할 수 있습니다.

 

정의

우선 애트리뷰트는 System.Attribute 클래스를 상속 받아 정의합니다. 이 단계에서 애트리뷰트의 생성자와 프로퍼티 등을 정의할 수 있습니다.

using System;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class MyCustomAttribute : Attribute
{
    public string Message { get; }

    public MyCustomAttribute(string message)
    {
        Message = message;
    }
}

 

AttributeUsage 애트리뷰트는 생성할 애트리뷰트의 세부적인 설정을 할 수 있게 해줍니다. 위 예시에서는 정의한 애트리뷰트의 적용대상을 클래스와 메서드로 하겠다는 의미입니다. 그리고 생성자를 통해 외부의 추가적인 정보를 저장 할 수 있습니다.

 

적용

[MyCustom("This class is important")]
class MyClass
{
    [MyCustom("This method is going to do the important tasks")]
    public void ImportantMethod()
    {
        Console.WriteLine("Doing the important tasks");
    }
}

 

정의한 애트리뷰트를 적용했습니다. 이 후 컴파일러는 내부적으로 메타데이터를 저장하여 런타임에는 리플렉션을 통해 애트리뷰트 정보를 읽을 수 있습니다.

 

조회

이제 런타임에 리플렉션을 통해 애트리뷰트를 읽어 보겠습니다.

using System;
using System.Reflection;

class Program
{
    static void Main()
    {
        Type type = typeof(MyClass);

        object[] classAttributes = type.GetCustomAttributes(typeof(MyCustomAttribute), false);
        foreach (MyCustomAttribute attr in classAttributes)
        {
            Console.WriteLine($"class attribute: {attr.Message}");
        }

        MethodInfo method = type.GetMethod("ImportantMethod");
        object[] methodAttributes = method.GetCustomAttributes(typeof(MyCustomAttribute), false);
        foreach (MyCustomAttribute attr in methodAttributes)
        {
            Console.WriteLine($"method attribute: {attr.Message}");
        }
    }
}

 

class attribute: This class is important
method attribute: This method is going to do the important tasks

 

위 예시에서는 리플렉션에서 배웠던 것 처럼 애트리뷰트가 적용된 클래스의 Type을 가져와 클래스와 메서드에 새롭게 정의한 애트리뷰트들을 조회하는 코드입니다.

 

GetCustomAttributes는 사용자 지정 애트리뷰트를 검색하는 메서드로 첫번째 매개변수에는 찾을 사용자 지정 애트리뷰트의 타입이 들어갑니다. 두번째에는 찾을 사용자 지정 애트리뷰트의 상위 사용자 지정 애트리뷰트도 포함하여 검색할지를 정합니다. true로 설정할 경우 검색 범위에 상위 항목도 포함하게 되고, false로 설정할 경우 검색 범위에 상위 항목은 포함되지 않습니다.

'개발 > C#' 카테고리의 다른 글

C# 입출력 작업  (0) 2025.03.09
C# dynamic  (0) 2025.03.05
C# 리플렉션  (0) 2025.02.23
C# LINQ  (0) 2025.02.21
C# 이벤트  (0) 2025.02.20