C#

c# - reflection과 Attribute에 대해 알아보자

근본넘치는개발자 2024. 10. 23. 23:48

오늘은 reflection과 Attribute에 대해 알아보고자 합니다.

 

1주 동안 만든 팀 과제를 제출하고 다른 팀들의 발표를 듣던 중
reflection을 사용하는 코드를 보게 되었습니다.

 

이름만 들어보고 사용을 한 번도 안 해봐서 이참에

어떻게 사용하는지 간단하게 찾아보고 정리해 봤습니다.

 

자료를 찾다 보니 다들 Attribute를 묶어서 설명하더군요.

일단 이것도 함께 정리해 봤습니다. 

 

reflection 

런타임 중에 객체의 형식을 가져와 사용할 수 있게 해주는 기능입니다.

 

관련 메서드들을 claude를 통해 깔끔하게 표로 정리해 달라고 해봤습니다.

 

타입을 가져오는 방식은 3가지로 나뉩니다.

메서드   반환 타입 설명 예제
GetType() Type 객체의 실제 런타임 타입을 반환 string str = "Hello"; Type t = str.GetType();
typeof() Type 컴파일 타임에 타입 정보를 가져옴 Type t = typeof(string);
Type.GetType(string) Type 문자열로 된 타입 이름으로 Type을 가져옴 Type t = Type.GetType("System.String");

 

자주 사용되는 GetType들 입니다. 

메서드   반환 타입 설명 예제
GetMethods() MethodInfo[] 모든 public 메서드 정보를 가져옴 var methods = type.GetMethods();
GetMethod(string) MethodInfo 특정 이름의 public 메서드 정보를 가져옴 var method = type.GetMethod("ToString");
GetProperties() PropertyInfo[] 모든 public 속성 정보를 가져옴 var props = type.GetProperties();
GetProperty(string) PropertyInfo 특정 이름의 public 속성 정보를 가져옴 var prop = type.GetProperty("Length");
GetFields() FieldInfo[] 모든 public 필드 정보를 가져옴 var fields = type.GetFields();
GetField(string) FieldInfo 특정 이름의 public 필드 정보를 가져옴 var field = type.GetField("fieldName");
GetConstructors() ConstructorInfo[] 모든 public 생성자 정보를 가져옴 var ctors = type.GetConstructors();
GetConstructor(Type[]) ConstructorInfo 특정 매개변수를 가진 생성자 정보를 가져옴 var ctor = type.GetConstructor(new Type[] { typeof(string) });
GetCustomAttributes() object[] 모든 커스텀 속성을 가져옴 var attrs = type.GetCustomAttributes();
GetInterface(string) Type 특정 이름의 인터페이스 타입을 가져옴 var iface = type.GetInterface("IDisposable");
GetInterfaces() Type[] 구현된 모든 인터페이스를 가져옴 var ifaces = type.GetInterfaces();

 

가져올 형식에 추가 옵션 (BindingFlags)을 걸 수 있습니다.

플래그 설명
Public public 멤버만
NonPublic private/protected 멤버
Instance 인스턴스 멤버
Static 정적 멤버
DeclaredOnly 상속된 멤버 제외
IgnoreCase 멤버 이름 대소문자 구분 없이

 

// 모든 private 인스턴스 필드 가져오기
var privateFields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);

// 상속된 것을 제외한 public 메서드만 가져오기
var declaredPublicMethods = type.GetMethods(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance);

 

 

보통 가져온 객체 형식을 토대로

Activator라는 클래스를 사용하여 객체의 인스턴스를 동적으로 생성하고,

GetValue와 SetValue를 통해 값을 제어하여 사용합니다.

 

public class Person
{
    public string Name { get; set; }
    private int age;
    public string Address { get; set; }
}

public class ReflectionExample
{
    public void DemoGetSetValues()
    {
        // Activator로 인스턴스 생성
        Type type = typeof(Person);
        Person person = (Person)Activator.CreateInstance(type);

        // 속성(Property) 값 설정/가져오기
        PropertyInfo nameProperty = type.GetProperty("Name");
        nameProperty.SetValue(person, "John");  // 값 설정
        string name = (string)nameProperty.GetValue(person);  // 값 가져오기
        Console.WriteLine($"Name: {name}");

        // private 필드 값 설정/가져오기
        FieldInfo ageField = type.GetField("age", 
            BindingFlags.NonPublic | BindingFlags.Instance);
        ageField.SetValue(person, 25);  // private 필드 값 설정
        int age = (int)ageField.GetValue(person);  // private 필드 값 가져오기
        Console.WriteLine($"Age: {age}");
    }
}

 

Attribute는 주로 

 

  • 플러그인 시스템 구현
  • 직렬화/역직렬화
  • DI(종속성 주입) 컨테이너

 

등에 사용된다고 합니다.

 

※ Reflection을 사용하기 위해서는

using System.Reflection; 을 추가해 줘야 합니다.

 

Attribute

c# 코드에 추가할 수 있는 메타데이터 

 

메타데이터?

다른 데이터를 설명해 주는 데이터

 

주석은 컴파일 시 실행파일에서 제거되지만,

어트리뷰트는 실행파일 안에 저장되어 런타임 중에도 읽을 수 있습니다.

 

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

}

//Obsolete는 코드가 더 이상 사용되지 않으며 
//더 이상 사용되어서는 안된다고 알리기 위해 사용하는 Attribute
// 마이크로 소프트 공식 문서 코드 발췌

 

 

이 외에도 유니티에서의 SerializeField가 Attribute의 예에 해당한다고 합니다.

오...

 

정해진 Attribute 말고 직접 만들어 사용할 수는 없을까요?

당연히 가능합니다. 

 

public class GotchaAttribute : Attribute
{
    public GotchaAttribute(string str)
    {
    }
}

// C#에서 Attribute는 Attribute 기본 클래스에서 상속되는 클래스
// 마이크로 소프트 공식 문서 코드 발췌

public class Explane
{

	// 만든  GotchaAttribute 어트리뷰트 사용, Gotcha 뒤 Attribute는 생략 가능 
	[Gotcha("string이라서 문자열 ")] 


}

 

마무리

 

reflection과 함께 활용하여 런타임으로 Attribute에 접근,

동적 할당하는 방식으로 자주 사용되어 둘을 함께 설명하나 봅니다.

왜 같이 설명했는지 이제야 이해가 가는군요.

 

최근 유니티에서 동적으로 어떻게 연동해야 할지 고민 중이었는데 

한번 적용해 코드를 짜봐야 할 것 같습니다.

여기에 추가로 직렬화하여 데이터 저장 때도 사용되는 듯 하니 ;; 

 

배울수록 모르는 게 너무 많네요;;

참고한(혹은 참고하면 좋은) 자료

 

https://youtu.be/y96OaJXKM7o?si=TuWnqlQyhg8dUSuN

 

https://youtu.be/x0CayxlgKPE?si=8JCfdF0ZL--1EvLp

 


https://youtu.be/aDGvR5Fk9J4?si=ahyyMQ-ayYzaz-N3

https://learn.microsoft.com/ko-kr/dotnet/csharp/advanced-topics/reflection-and-attributes/

 

특성 및 리플렉션 - C#

특성을 사용하여 C#에서 메타데이터 또는 선언적 정보를 코드와 연결합니다. 특성은 리플렉션을 사용하여 런타임에 쿼리할 수 있습니다. 리플렉션은 C#에서 어셈블리, 모듈 및 형식을 설명하는

learn.microsoft.com