일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 구조체
- Houdini
- github
- Data Structure
- 플러터
- c언어
- gitlab
- C언어 포인터
- Flutter
- Algorithm
- c# 윈폼
- 유니티
- vim
- git
- c#
- 도커
- docker
- 깃
- HTML
- jupyter
- dart 언어
- jupyter lab
- C# delegate
- C++
- Unity
- Python
- c# winform
- c# 추상 클래스
- 포인터
- 다트 언어
- Today
- Total
nomad-programmer
[Programming/C#] 어트리뷰트 (Attribute) 본문
어트리뷰트(Attribute)는 코드에 대한 부가 정보를 기록하고 읽을 수 있는 기능이다.
어트리뷰트가 주석과 다른 점은 주석이 사람이 읽고 쓰는 정보라면, 어트리뷰트는 사람이 작성하고 컴퓨터가 읽는다는 것이다.
어트리뷰트를 이용해서 클래스나 구조체, 메소드, 프로퍼티 등에 데이터를 기록해두면 이 정보를 C# 컴파일러나 C#으로 작성된 프로그램이 이 정보를 읽어 사용할 수 있다. (예를 들어 컴파일러가 경고를 내도록 만들 수도 있음)
메타데이터 (Metadata)
메타데이터란 데이터의 데이터를 말한다. 가령 C# 코드도 데이터지만, 이 코드에 대한 정보, 데이터가 있을 수 있다. 이를 메타데이터라고 하는 것이다.
어트리뷰트나 리플렉션을 통해 얻는 정보들도 C# 코드의 메타데이터라고 할 수 있다.
어트리뷰트 사용하는 방법
어트리뷰트를 사용할 때는 설명을 하고자 하는 코드 요소 앞에 [ ] 괄호 쌍을 붙이고 그 안에 어트리뷰트의 이름을 넣으면 된다.
[ 어트리뷰트_이름(어트리뷰트 매개 변수) ]
public void MyMethod()
{
// ...
}
아래는 컴파일러가 경고 메시지를 내도록 하는 예제 코드이다. .NET 프레임워크에서 기본적으로 제공하는 Obsolete 어트리뷰트를 이용
class MyClass
{
[Obsolete("OldMethod는 오래되었습니다. NewMethod()를 이용하십시오.")]
public void OldMethod()
{
Console.WriteLine("old version...");
}
public void NewMethod()
{
Console.WriteLine("new version...");
}
}
OldMethod()를 사용하는 코드를 그대로 둔 채 컴파일을 하게 된다면 "OldMethod는 오래되었습니다. NewMethod()를 이용하십시오."라는 경고 메시지를 보게 될 것이다.
using System;
namespace test
{
class MyClass
{
[Obsolete("OldMethod는 오래되었습니다. NewMethod()를 이용하십시오.")]
public void OldMethod()
{
Console.WriteLine("old version...");
}
public void NewMethod()
{
Console.WriteLine("new version...");
}
}
internal class Program
{
public static void Main(string[] args)
{
MyClass obj = new MyClass();
obj.OldMethod();
obj.NewMethod();
}
}
}
/* 결과
old version...
new version...
*/
/* 경고
Program.cs(24, 13): [CS0618] 'MyClass.OldMethod()' is obsolete: 'OldMethod는 오래되었습니다. NewMethod()를 이용하십시오.'
*/
정상적으로 실행하지만, 컴파일을 할 때 오류 목록창을 확인하면 경고 메시지가 나타나는 것을 볼 수 있다.
호출자 정보 어트리뷰트
C/C++ 언어의 __FILENAME__, __LINE__, __FUNCTION__ 이다.
C# 5.0 버전부터 호출자 정보(Caller Information)가 도입됐다. 호출자 정보는 메소드의 매개 변수에 사용되며 메소드의 호출자 이름, 호출자 메소드가 정의되어 있는 소스 파일 경로, 소스 파일 내의 행(Line) 번호를 알 수 있다.
이것을 이용해서 응용 프로그램의 이벤트를 로그 파일이나 화면에 출력하면 그 이벤트가 어떤 코드에서 일어났는지를 알 수 있다.
어트리뷰트 | 설명 |
CallerMemberNameAttribute | 현재 메소드를 호출한 메소드 또는 프로퍼티의 이름을 나타낸다. |
CallerFilePathAttribute | 현재 메소드가 호출된 소스 파일 경로를 나타낸다. 이 때 경로는 소스 코드를 컴파일할 때의 전체 경로를 나타낸다. |
CallerLineNumberAttribute | 현재 메소드가 호출된 소스 파일 내의 행(Line) 번호를 나타낸다. |
호출자 정보 어트리뷰트 사용 예제
using System;
using System.Runtime.CompilerServices;
namespace test
{
public static class Trace
{
public static void WriteLine(string message,
[CallerFilePath] string file = "",
[CallerLineNumber] int line = 0,
[CallerMemberName] string member = "")
{
Console.WriteLine($"{file}(Line: {line}) {member}: {message}");
}
}
internal class Program
{
public static void Main(string[] args)
{
Trace.WriteLine("C#은 정말 재밌다!");
}
}
}
/* 결과
/home/test/workspace/c#/test/Program.cs(Line: 21) Main: C#은 정말 재밌다!
*/
Trace.WriteLine()의 선언부를 보면 [CallerFilePath], [CallerLineNumer], [CallerMemberName] 이 선택적 매개 변수로 사용되고 있다. 이렇게 하면 Trace.WriteLine() 메소드를 호출할 때 호출자 정보 어트리뷰트로 수식한 매개 변수는 프로그래머가 별도로 입력하지 않아도 된다.
사용자 정의 어트리뷰트
.NET 프레임워크가 제공하는 어트리뷰트는 Obsolete 말고도 그 종류가 상당히 많다.
- [DLLImport] : C/C++ 언어로 작성된 네이티브 DLL(Dynamic Link Library) 에 존재하는 함수를 호출할 때 사용한다.
- [Conditional] : 조건부 메소드 실행을 지정할 때 사용한다.
어트리뷰트도 하나의 클래스일 뿐이다. 모든 어트리뷰트는 예를 들어 다음과 같이 System.Attribute 클래스로부터 상속을 받아 만들어진다.
Class History : System.Attribute
{
// ...
}
System.Attribute를 상속하는 것만으로도 어트리뷰트 하나를 만든 셈이 된다. 이렇게 선언된 어트리뷰트는 Obsolete 어트리뷰트처럼 [ ] 괄호 안에 어트리뷰트 이름을 넣어 사용하면 된다.
[HIstory]
class MyClass
{
// ...
}
위의 History는 어트리뷰트이긴 하지만 아무것도 설명하는 것이 없다. History 어트리뷰트가 구체적으로 설명할 내용과 이것을 "어떻게" 설명하게 할 것인가를 나타내도록 코드를 추가해보자.
History가 클래스의 변경 이력을 나타내도록 할 것이다. History 클래스에 설명할 클래스의 작성자(즉, 프로그래머), 버전, 그리고 변경 내용 등을 나태낼 수 있도록 필드 및 프로퍼티를 추가할 것이다.
class History : System.Attribute
{
private string programmer;
public double Version{ get; set; }
public string Changes{ get; set; }
// 생성자
public History(string programmer)
{
this.programmer = programmer;
Version = 1.0;
Changes = "First release";
}
public string Programmer => programmer;
}
History 클래스는 System.Attribute로부터 상속받았을 뿐이다. 여느 클래스와 다를 바가 없다. History 클래스를 사용해보자.
[History("Test1", Version = 0.1 Changes = "2020-05-05 Created class stub")]
class MyClass
{
public void Func()
{
Console.WriteLine("Func()");
}
}
이와 같이 MyClass를 History 어트리뷰트로 설명해 놓으면 리플렉션을 이용해서 손쉽게 Release 노트를 만들 수 있다.
하지만 한 가지 문제가 존재한다. 훗날 다른 프로그래머가 MyClass를 수정한 뒤 History 어트리뷰트를 추가하고 싶어도 더 추가할 수 없다.
지금의 History 어트리뷰트는 단 한번밖에 사용할 수 없기 때문이다. 가령 다음과 같이 어트리뷰트를 사용하려 들면 컴파일러가 에러 메시지를 보여줄 것이다.
[History("Test1", Version = 0.1, Changes = "2020-05-05 Created class stub")]
// 현재로서는 어트리뷰트를 이렇게 겹쳐 사용할 수 없다.
[History("Test2", Version = 0.2, changes = "2020-07-05 Added Func() Method")]
class MyClass
{
public void Func() { }
}
이 문제를 해결하려면 System.AttributeUsage 라는 어트리뷰트의 도움을 받아야만 한다.
System.AttributeUsage 는 어트리뷰트의 어트리뷰트다. 어트리뷰트가 어떤 대상을 설명할지, 이 어트리뷰트를 중복해서 사용할 수 있는지의 여부 등을 설명한다. System.AttributeUsage는 다음과 같이 어트리뷰트 정의 부분에 사용하면 된다.
[System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple=true)]
class History : System.Attribute
{
// ...
}
System.AttributeUsage의 첫 번째 매개 변수는 지금 선언하고 있는 어트리뷰트의 설명 대상이 무엇인지를 나타낸다. 이것을 Attribute Target이라고 하는데, 어트리뷰트의 설명 대상이 될 수 있는 코드 요소는 다음과 같다.
Attribute Target | 설명 |
All | 이 표의 나머지 모든 요소 |
Assembly | 어셈블리 |
Module | 모듈 |
Interface | 인터페이스 |
Class | 클래스 |
Struct | 구조체 |
ClassMembers | 클래스 안에 선언된 클래스나 구조체를 포함한 클래스의 모든 멤버 |
Constructor | 생성자 |
Delegate | 대리자 |
Enum | 열거형 |
Event | 이벤트 |
Field | 필드 |
Property | 프로퍼티 |
Method | 메소드 |
Parameter | 메소드의 매개 변수 |
ReturnValue | 메소드의 반환 값 |
위의 Attribute Target은 논리합 연산자를 이용해서 결합할 수도 있다. 가령 클래스와 메소드를 대상으로 하고 싶다면 AttributeTargets.Class | AttributeTargets.Method를 System.AttributeUsage의 어트리뷰트 매개 변수로 넘기면 된다.
[System.AttributeUsage(
System.AttributeTargets.Class | System.AttributeTargets.Method,
AllowMultiple=true)]
class History : System.Attribute
{
// ...
}
AllowMultiple 이 매개변수는 History 어트리뷰트를 여러 번 사용할 수 있도록 하기 위한 매개변수이다. true를 대입하면 중복해서 사용할 수 있다.
사용자 정의 Attribute 사용 예제
using System;
namespace test
{
[System.AttributeUsage(
System.AttributeTargets.Class, AllowMultiple = true)]
class History : System.Attribute
{
private string programmer;
public double Version { get; set; }
public string Changes { get; set; }
// 생성자
public History(string programmer)
{
this.programmer = programmer;
Version = 1.0;
Changes = "First release";
}
public string Programmer => programmer;
}
[History("Jeon", Version = 0.1, Changes = "2020-05-05 Created class stub")]
[History("Ethan", Version = 0.2, Changes = "2020-07-05 Added Func() Method")]
class MyClass
{
public void Func()
{
Console.WriteLine("Func()");
}
}
internal class Program
{
public static void Main(string[] args)
{
Type type = typeof(MyClass);
Attribute[] attributes = Attribute.GetCustomAttributes(type);
Console.WriteLine("MyClass change history...");
foreach (Attribute attr in attributes)
{
History hist = attr as History;
if (hist != null)
{
Console.WriteLine("Ver: {0}, Programmer: {1}, Changes: {2}",
hist.Version, hist.Programmer, hist.Changes);
}
}
}
}
}
/* 결과
MyClass change history...
Ver: 0.1, Programmer: Jeon, Changes: 2020-05-05 Created class stub
Ver: 0.2, Programmer: Ethan, Changes: 2020-07-05 Added Func() Method
*/
'Programming > C#' 카테고리의 다른 글
[Programming/C#] COM (Component Object Model) (0) | 2020.09.27 |
---|---|
[Programming/C#] dynamic 형식 (0) | 2020.09.26 |
[Programming/C#] 리플렉션 (Reflection) (0) | 2020.09.25 |
[Programming/C#] LINQ의 표준 연산자와 쿼리식 문법 (0) | 2020.09.25 |
[Programming/C#] LINQ (링크) (0) | 2020.09.24 |