C#에서 특성 사용Use Attributes in C#

특성은 선언적으로 정보를 코드와 연결하는 방법을 제공합니다.Attributes provide a way of associating information with code in a declarative way. 다양한 대상에 적용할 수 있는 재사용 가능 요소를 제공할 수도 있습니다.They can also provide a reusable element that can be applied to a variety of targets.

[Obsolete] 특성을 고려하세요.Consider the [Obsolete] attribute. 이 특성은 클래스, 구조체, 메서드, 생성자 등에 적용될 수 있으며It can be applied to classes, structs, methods, constructors, and more. 요소가 더 이상 필요하지 않다는 사실을 _선언_합니다.It declares that the element is obsolete. 이 특성을 찾고 응답으로 특정 작업을 수행하는 것은 모두 C# 컴파일러가 진행합니다.It's then up to the C# compiler to look for this attribute, and do some action in response.

이 자습서에서는 코드에 특성을 추가하는 방법, 사용자 지정 특성을 만들고 사용하는 방법, .NET Core로 빌드되는 일부 특성을 사용하는 방법을 소개합니다.In this tutorial, you'll be introduced to how to add attributes to your code, how to create and use your own attributes, and how to use some attributes that are built into .NET Core.

사전 요구 사항Prerequisites

.NET Core를 실행하려면 컴퓨터에 설정해야 합니다.You’ll need to set up your machine to run .NET core. .NET Core 다운로드 페이지에서 설치 지침을 찾을 수 있습니다.You can find the installation instructions on the .NET Core Downloads page. Windows, Ubuntu Linux, macOS 또는 Docker 컨테이너에서 이 애플리케이션을 실행할 수 있습니다.You can run this application on Windows, Ubuntu Linux, macOS or in a Docker container. 선호하는 코드 편집기를 설치해야 합니다.You’ll need to install your favorite code editor. 아래 설명에서는 오픈 소스 플랫폼 간 편집기인 Visual Studio Code를 사용합니다.The descriptions below use Visual Studio Code which is an open source, cross platform editor. 그러나 익숙한 어떤 도구도 사용 가능합니다.However, you can use whatever tools you are comfortable with.

애플리케이션 만들기Create the Application

이제 모든 도구를 설치했으므로 새로운 .NET Core 애플리케이션을 만들어 보겠습니다.Now that you've installed all the tools, create a new .NET Core application. 명령줄 생성기를 사용하려면 즐겨 사용하는 셸에서 다음 명령을 실행합니다.To use the command line generator, execute the following command in your favorite shell:

dotnet new console

이 명령은 기본 .NET Core 프로젝트 파일을 만듭니다.This command will create bare-bones .NET core project files. dotnet restore를 실행하여 이 프로젝트를 컴파일하는 데 필요한 종속성을 복원해야 합니다.You will need to execute dotnet restore to restore the dependencies needed to compile this project.

참고

.NET Core 2.0 SDK부터 복원을 수행해야 하는 모든 명령(예: dotnet new, dotnet builddotnet run)에서 암시적으로 실행되므로 dotnet restore를 실행할 필요가 없습니다.Starting with .NET Core 2.0 SDK, you don't have to run dotnet restore because it's run implicitly by all commands that require a restore to occur, such as dotnet new, dotnet build and dotnet run. Azure DevOps Services의 연속 통합 빌드와 같이 명시적 복원을 수행하는 것이 올바른 특정 시나리오 또는 복원이 발생하는 시간을 명시적으로 제어해야 하는 빌드 시스템에서는 여전히 유효한 명령입니다.It's still a valid command in certain scenarios where doing an explicit restore makes sense, such as continuous integration builds in Azure DevOps Services or in build systems that need to explicitly control the time at which the restore occurs.

이 프로그램을 실행하려면 dotnet run을 사용합니다.To execute the program, use dotnet run. 콘솔에 "Hello, World" 출력이 표시됩니다.You should see "Hello, World" output to the console.

코드에 특성을 추가하는 방법How to add attributes to code

C#에서 특성은 Attribute 기본 클래스에서 상속되는 클래스입니다.In C#, attributes are classes that inherit from the Attribute base class. Attribute에서 상속되는 모든 클래스는 코드의 다른 부분에서 일종의 "태그"로 사용될 수 있습니다.Any class that inherits from Attribute can be used as a sort of "tag" on other pieces of code. 예를 들어 ObsoleteAttribute라는 특성이 있습니다.For instance, there is an attribute called ObsoleteAttribute. 이 특성은 코드가 더 이상 사용되지 않으며 더 이상 사용할 수 없음을 알리기 위해 사용됩니다.This is used to signal that code is obsolete and shouldn't be used anymore. 예를 들어 대괄호를 사용하여 클래스에 이 특성을 배치할 수 있습니다.You can place this attribute on a class, for instance, by using square brackets.

[Obsolete]
public class MyClass
{
}

이 클래스는 ObsoleteAttribute로 지칭되지만 코드에서 [Obsolete]를 사용하는 데만 필요합니다.Note that while the class is called ObsoleteAttribute, it's only necessary to use [Obsolete] in the code. 이것이 C#에서 준수하는 규칙입니다.This is a convention that C# follows. 원할 경우 전체 이름 [ObsoleteAttribute]를 사용할 수 있습니다.You can use the full name [ObsoleteAttribute] if you choose.

클래스를 더 이상 사용되지 않는 것으로 표시할 경우 더 이상 사용되지 않는 이유 및/또는 대싱 사용할 항목에 대한 정보를 제공하는 것이 좋습니다.When marking a class obsolete, it's a good idea to provide some information as to why it's obsolete, and/or what to use instead. 이 작업을 위해 Obsolete 특성에 문자열 매개 변수를 전달합니다.Do this by passing a string parameter to the Obsolete attribute.

[Obsolete("ThisClass is obsolete. Use ThisClass2 instead.")]
public class ThisClass
{
}

이 문자열은 var attr = new ObsoleteAttribute("some string")를 작성하는 것처럼 ObsoleteAttribute 생성자에 인수로 전달됩니다.The string is being passed as an argument to an ObsoleteAttribute constructor, just as if you were writing var attr = new ObsoleteAttribute("some string").

특성 생성자에 대한 매개 변수는 단순 형식/리터럴인 bool, int, double, string, Type, enums, etc 및 해당 형식의 배열로 제한됩니다.Parameters to an attribute constructor are limited to simple types/literals: bool, int, double, string, Type, enums, etc and arrays of those types. 식 또는 변수는 사용할 수 없습니다.You can not use an expression or a variable. 위치 또는 명명된 매개 변수는 얼마든지 사용할 수 있습니다.You are free to use positional or named parameters.

고유한 특성을 만드는 방법How to create your own attribute

특성을 만드는 과정은 Attribute 기본 클래스에서 상속하는 것만큼 간단합니다.Creating an attribute is as simple as inheriting from the Attribute base class.

public class MySpecialAttribute : Attribute
{
}

위에 따라, 이제 코드베이스의 어디에서든지 [MySpecial](또는 [MySpecialAttribute])을 특성으로 사용할 수 있습니다.With the above, I can now use [MySpecial] (or [MySpecialAttribute]) as an attribute elsewhere in the code base.

[MySpecial]
public class SomeOtherClass
{
}

ObsoleteAttribute 트리거 같은 .NET 기본 클래스 라이브러리의 특성은 컴파일러 내에 특정 동작을 포함합니다.Attributes in the .NET base class library like ObsoleteAttribute trigger certain behaviors within the compiler. 그러나 만드는 특성은 메타데이터의 역할만 수행하며 특성 클래스 내의 코드는 실행되지 않습니다.However, any attribute you create acts only as metadata, and doesn't result in any code within the attribute class being executed. 코드의 임의 위치에서 해당 메타데이터에 대해 작업을 수행할 수 있습니다(이 자습서의 뒷부분에 좀 더 자세히 설명되어 있음).It's up to you to act on that metadata elsewhere in your code (more on that later in the tutorial).

여기에는 확인해 볼만한 '과제'가 제공됩니다.There is a 'gotcha' here to watch out for. 위에서 설명한 것처럼 특성을 사용할 때는 특정 형식만 인수로 전달되도록 허용됩니다.As mentioned above, only certain types are allowed to be passed as arguments when using attributes. 그러나 특성 유형을 만들 때 C# 컴파일러는 사용자가 해당 매개 변수를 만들지 못하게 하지 않습니다.However, when creating an attribute type, the C# compiler won't stop you from creating those parameters. 아래 예제에서는 적절히 컴파일을 수행하는 생성자로 특성을 만들었습니다.In the below example, I've created an attribute with a constructor that compiles just fine.

public class GotchaAttribute : Attribute
{
    public GotchaAttribute(Foo myClass, string str) {
    }
}

그러나 이 생성자를 특성 구문에서 사용할 수는 없습니다.However, you will be unable to use this constructor with attribute syntax.

[Gotcha(new Foo(), "test")] // does not compile
public class AttributeFail
{
}

위에서는 Attribute constructor parameter 'myClass' has type 'Foo', which is not a valid attribute parameter type과 같은 컴파일러 오류가 발생합니다.The above will cause a compiler error like Attribute constructor parameter 'myClass' has type 'Foo', which is not a valid attribute parameter type

특성 사용을 제한하는 방법How to restrict attribute usage

특성은 다양한 "대상"에 사용할 수 있습니다.Attributes can be used on a number of "targets". 위의 예제에서는 클래스에 대한 특성만 표시하지만 다음에도 사용될 수 있습니다.The above examples show them on classes, but they can also be used on:

  • AssemblyAssembly
  • 클래스Class
  • 생성자Constructor
  • 대리자Delegate
  • EnumEnum
  • 이벤트Event
  • 필드Field
  • GenericParameterGenericParameter
  • 인터페이스Interface
  • 메서드Method
  • ModuleModule
  • 매개 변수Parameter
  • 속성Property
  • ReturnValueReturnValue
  • 구조체Struct

특성 클래스를 만들 때 기본적으로 C#은 허용 가능한 특성 대상 중 하나에 대해 해당 특성을 사용할 수 있습니다.When you create an attribute class, by default, C# will allow you to use that attribute on any of the possible attribute targets. 특성을 특정 대상으로 제한하려는 경우 특성 클래스에 대해 AttributeUsageAttribute를 사용하면 됩니다.If you want to restrict your attribute to certain targets, you can do so by using the AttributeUsageAttribute on your attribute class. 맞습니다. 특성에 대한 특성입니다.That's right, an attribute on an attribute!

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class MyAttributeForClassAndStructOnly : Attribute
{
}

클래스 또는 구조체 이외의 항목에 대해 위 특성을 적용하려고 하면 Attribute 'MyAttributeForClassAndStructOnly' is not valid on this declaration type. It is only valid on 'class, struct' declarations와 같은 컴파일러 오류가 발생합니다.If you attempt to put the above attribute on something that's not a class or a struct, you will get a compiler error like Attribute 'MyAttributeForClassAndStructOnly' is not valid on this declaration type. It is only valid on 'class, struct' declarations

public class Foo
{
    // if the below attribute was uncommented, it would cause a compiler error
    // [MyAttributeForClassAndStructOnly]
    public Foo() 
    { }
}

코드 요소에 연결된 특성을 사용하는 방법How to use attributes attached to a code element

특성는 메타데이터로 작동합니다.Attributes act as metadata. 외부 영향이 없으면 어떤 작업도 수행하지 않습니다.Without some outward force, they won't actually do anything.

특성을 찾아 작업을 수행하려면 일반적으로 리플렉션이 필요합니다.To find and act on attributes, Reflection is generally needed. 이 자습서에서는 리플렉션을 자세히 다루지 않겠지만, 기본 개념은 리플렉션을 사용하면 다른 코드를 검사하는 코드를 C#에서 작성할 수 있다는 것입니다.I won't cover Reflection in-depth in this tutorial, but the basic idea is that Reflection allows you to write code in C# that examines other code.

예를 들어 리플렉션을 사용하여 클래스에 대한 정보(코드 헤드에 using System.Reflection; 추가)를 가져올 수 있습니다.For instance, you can use Reflection to get information about a class(add using System.Reflection; at the head of your code):

TypeInfo typeInfo = typeof(MyClass).GetTypeInfo();
Console.WriteLine("The assembly qualified name of MyClass is " + typeInfo.AssemblyQualifiedName);

다음과 같이 출력됩니다.The assembly qualified name of MyClass is ConsoleApplication.MyClass, attributes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nullThat will print out something like: The assembly qualified name of MyClass is ConsoleApplication.MyClass, attributes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

TypeInfo 개체(또는 MemberInfo, FieldInfo 등)가 있으면 GetCustomAttributes 메서드를 사용할 수 있습니다.Once you have a TypeInfo object (or a MemberInfo, FieldInfo, etc), you can use the GetCustomAttributes method. 그러면 Attribute 개체 컬렉션이 반환됩니다.This will return a collection of Attribute objects. GetCustomAttribute를 사용하고 특성 유형을 지정할 수도 있습니다.You can also use GetCustomAttribute and specify an Attribute type.

MyClassMemberInfo 인스턴스에 대해 GetCustomAttributes를 사용하는 예제는 다음과 같습니다(앞에서 살펴본 [Obsolete] 특성을 포함하는 예제).Here's an example of using GetCustomAttributes on a MemberInfo instance for MyClass (which we saw earlier has an [Obsolete] attribute on it).

var attrs = typeInfo.GetCustomAttributes();
foreach(var attr in attrs)
    Console.WriteLine("Attribute on MyClass: " + attr.GetType().Name);

해당 내용은 콘솔에 Attribute on MyClass: ObsoleteAttribute와 같이 표시됩니다.That will print to console: Attribute on MyClass: ObsoleteAttribute. MyClass에 다른 특성을 추가해 보세요.Try adding other attributes to MyClass.

이러한 Attribute 개체가 지연되어 인스턴스화되는 것에 유의해야 합니다.It's important to note that these Attribute objects are instantiated lazily. 즉, GetCustomAttribute 또는 GetCustomAttributes를 사용해야만 인스턴스화됩니다.That is, they won't be instantiated until you use GetCustomAttribute or GetCustomAttributes. 또한 매번 인스턴스화되기도 합니다.They are also instantiated each time. GetCustomAttributes를 연속해서 2번 호출하면 2개의 다른 ObsoleteAttribute 인스턴스가 반환됩니다.Calling GetCustomAttributes twice in a row will return two different instances of ObsoleteAttribute.

BCL(기본 클래스 라이브러리)의 공통 특성Common attributes in the base class library (BCL)

특성은 많은 도구 및 프레임워크에서 사용됩니다.Attributes are used by many tools and frameworks. NUnit은 NUnit Test Runner에서 사용되는 [Test][TestFixture] 같은 특성을 사용합니다.NUnit uses attributes like [Test] and [TestFixture] that are used by the NUnit test runner. ASP.NET MVC는 [Authorize]와 같은 특성을 사용하고 MVC 작업에 대해 크로스 커팅(Cross-Cutting) 문제를 해결하기 위한 작업 필터 프레임워크를 제공합니다.ASP.NET MVC uses attributes like [Authorize] and provides an action filter framework to perform cross-cutting concerns on MVC actions. PostSharp은 특성 구문을 사용하여 C#을 사용한 AOP(Aspect-Oriented Programming)를 허용합니다.PostSharp uses the attribute syntax to allow aspect-oriented programming in C#.

.NET Core 기본 클래스 라이브러리에 기본 제공되는 몇 가지 유의할 만한 특성은 다음과 같습니다.Here are a few notable attributes built into the .NET Core base class libraries:

  • [Obsolete].[Obsolete]. 이 특성은 위 예제에서 사용되었으며 System 네임스페이스에 있습니다.This one was used in the above examples, and it lives in the System namespace. 기본 코드를 변경하는 방법에 대해 선언적 설명서를 제공하는 것이 유용합니다.It is useful to provide declarative documentation about a changing code base. 메시지는 문자열의 형태로 제공될 수 있으며 다른 부울 매개 변수가 컴파일러 경고를 컴파일러 오류로 에스컬레이션하는 데 사용될 수 있습니다.A message can be provided in the form of a string, and another boolean parameter can be used to escalate from a compiler warning to a compiler error.

  • [Conditional].[Conditional]. 이 특성은 System.Diagnostics 네임스페이스에 있습니다.This attribute is in the System.Diagnostics namespace. 이 특성은 메서드(또는 특성 클래스)에 적용할 수 있습니다.This attribute can be applied to methods (or attribute classes). 생성자에는 문자열을 전달해야 합니다.You must pass a string to the constructor. 해당 문자열이 #define 지시문과 일치하지 않는 경우 해당 메서드에 대한 호출(메서드 자체는 아님)이 C# 컴파일러에 의해 제거됩니다.If that string doesn't match a #define directive, then any calls to that method (but not the method itself) will be removed by the C# compiler. 일반적으로 이 특성은 디버깅(진단) 목적으로 사용됩니다.Typically this is used for debugging (diagnostics) purposes.

  • [CallerMemberName].[CallerMemberName]. 이 특성은 매개 변수에 사용될 수 있으며 System.Runtime.CompilerServices 네임스페이스에 있습니다.This attribute can be used on parameters, and lives in the System.Runtime.CompilerServices namespace. 다른 메서드를 호출하는 메서드의 이름을 삽입하는 데 사용되는 특성입니다.This is an attribute that is used to inject the name of the method that is calling another method. 일반적으로 다양한 UI 프레임워크에서 INotifyPropertyChanged를 구현하는 경우 '매직 문자열'을 제거하는 방법으로 사용됩니다.This is typically used as a way to eliminate 'magic strings' when implementing INotifyPropertyChanged in various UI frameworks. 예제:As an example:

public class MyUIClass : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private string _name;
    public string Name 
    {
        get { return _name;}
        set 
        {
            if (value != _name) 
            {
                _name = value;
                RaisePropertyChanged();   // notice that "Name" is not needed here explicitly
            }
        }
    }
}

위의 코드에서는 리터럴 "Name" 문자열이 없어도 됩니다.In the above code, you don't have to have a literal "Name" string. 이 경우 입력 관련 버그가 방지되며 좀 더 매끄러운 리팩터링/이름 바꾸기가 가능해집니다.This can help prevent typo-related bugs and also makes for smoother refactoring/renaming.

요약Summary

특성은 C#에 선언적 기능을 제공하지만 코드의 메타데이터 형식이며 단독으로 동작하지 않습니다.Attributes bring declarative power to C#, but they are a meta-data form of code and don't act by themselves.