C\#반사 에 대해 알 아야 할 것

12044 단어 C#반사
일반적으로 동적 으로 대상 의 유형,속성 과 방법 등 정 보 를 얻 는 데 반사 된다.오늘 너 를 데 리 고 반 사 를 해서 반 사 된 각종 흔 한 조작 을 모 아서 네가 모 르 는 것 이 있 는 지 없 는 지 주 워 보 자.
형식의 구성원 가 져 오기
Type 류 의 GetMembers 방법 은 이 유형의 모든 구성원 을 가 져 오 는 데 사 용 됩 니 다.방법 과 속성 을 포함 하여 BindingFlags 로 고 를 통 해 이 구성원 들 을 선별 할 수 있 습 니 다.

using System;
using System.Reflection;
using System.Linq;

public class Program
{
  public static voidMain()
  {
    var members = typeof(object).GetMembers(BindingFlags.Public |
      BindingFlags.Static | BindingFlags.Instance);
    foreach (var member in members)
    {
      Console.WriteLine($"{member.Name} is a {member.MemberType}");
    }
  }
}
출력:
GetType is a Method
GetHashCode is a Method
ToString is a Method
Equals is a Method
ReferenceEquals is a Method
.ctor is a Constructor
GetMembers 방법 은 BindingFlags 를 전달 하지 않 아 도 되 며,기본적으로 공 개 된 모든 멤버 에 게 돌아 갑 니 다.
대상 을 가 져 오고 호출 하 는 방법
Type 형식의 GetMethod 방법 은 이 유형의 MethodInfo 를 가 져 온 다음 MethodInfo 를 통 해 동적 으로 호출 할 수 있 습 니 다.
비정 상 방법 에 대해 서 는 해당 하 는 인 스 턴 스 를 매개 변수 로 전달 해 야 합 니 다.예제:

class Program
{
  public static void Main()
  {
    var str = "hello";
    var method = str.GetType()
      .GetMethod("Substring", new[] {typeof(int), typeof(int)});
    var result = method.Invoke(str, new object[] {0, 4}); //     str.Substring(0, 4)
    Console.WriteLine(result); //   :hell
  }
}
정적 방법 에 대해 대상 매개 변수 가 비어 있 습 니 다.예제:

var method = typeof(Math).GetMethod("Exp");
//     Math.Exp(2)
var result = method.Invoke(null, new object[] {2});
Console.WriteLine(result); //   (e^2):7.38905609893065
범 형 방법 이 라면 범 형 매개 변 수 를 통 해 범 형 방법 을 만들어 야 합 니 다.예제:

class Program
{
  public static void Main()
  {
    //         
    MethodInfo method1 = typeof(Sample).GetMethod("GenericMethod");
    MethodInfo generic1 = method1.MakeGenericMethod(typeof(string));
    generic1.Invoke(sample, null);

    //           
    MethodInfo method2 = typeof(Sample).GetMethod("StaticMethod");
    MethodInfo generic2 = method2.MakeGenericMethod(typeof(string));
    generic2.Invoke(null, null);
  }
}

public class Sample
{
  public void GenericMethod<T>()
  {
    //...
  }
  public static void StaticMethod<T>()
  {
    //...
  }
}
형식의 인 스 턴 스 를 만 듭 니 다.
반사 동 태 를 사용 하여 하나의 유형의 인 스 턴 스 를 만 드 는 데 여러 가지 방식 이 있 습 니 다.가장 간단 한 것 은new() 조건 으로 성명 하 는 것 이다.
new 조건 설명 사용 하기
하나의 방법 에서 인 스 턴 스 를 동적 으로 만들어 야 한다 면 new 조건 설명 을 직접 사용 할 수 있 습 니 다.예 를 들 어:

T GetInstance<T>() where T : new()
{
  T instance = newT();
  return instance;
}
그러나 이런 방식 은 장면 에 한계 가 있다.예 를 들 어 구조 함수 대 파라미터 의 유형 에 적용 되 지 않 는 다.
Activator 클래스 사용 하기
Activator 클래스 동적 으로 클래스 를 만 드 는 것 이 가장 흔 한 방법 입 니 다.예제:

Type type = typeof(BigInteger);
object result = Activator.CreateInstance(type);
Console.WriteLine(result); //   :0
result = Activator.CreateInstance(type, 123);
Console.WriteLine(result); //   :123
동적 으로 범 형 인 스 턴 스 를 만 들 려 면 먼저 개방 형(예 를 들 어List<>을 만 든 다음 에 범 형 매개 변수 에 따라 구체 적 인 범 형(예 를 들 어List<string>으로 전환 해 야 합 니 다.예제:

//        
Type openType = typeof(List<>);
//        
Type[] tArgs = { typeof(string) };
Type target = openType.MakeGenericType(tArgs);
//         
List<string> result = (List<string>)Activator.CreateInstance(target);
만약 당신 이 개방 형 과 구상 형 이 무엇 인지 모른다 면,본문의 마지막 절 을 보십시오.
구조 기 반사 사용
또한 반사 구조 기 를 통 해 동적 으로 클래스 를 만 들 수 있 습 니 다.위 에서 Activator 류 를 사용 하 는 것 보다 조금 번 거 롭 지만 성능 이 좋 습 니 다.예시:

ConstructorInfo c = typeof(T).GetConstructor(new[] { typeof(string) });
if (c == null)
  throw new InvalidOperationException("...");
T instance = (T)c.Invoke(new object[] { "test" });
FormatterServices 클래스 사용 하기
어떤 종류의 인 스 턴 스 를 만 들 려 면 구조 함수 와 속성 초기 화 를 실행 하지 않 고 Formatter Services 의 GetUninitialized Object 방법 을 사용 할 수 있 습 니 다.예시:

class Program
{
  static void Main()
  {
    MyClass instance = (MyClass)FormatterServices.GetUninitializedObject(typeof(MyClass));
    Console.WriteLine(instance.MyProperty1); //   :0
    Console.WriteLine(instance.MyProperty2); //   :0
  }
}

public class MyClass
{
  public MyClass(int val)
  {
    MyProperty1 = val < 1 ? 1 : val;
  }

  public int MyProperty1 { get; }

  public int MyProperty2 { get; set; } = 2;
}
속성 이나 방법의 강 한 유형 의뢰 획득
반사 로 대상 의 속성 과 방법 을 얻 은 후 강 한 유형의 방법 으로 접근 하거나 호출 하려 면 중간 에 의뢰 를 추가 할 수 있 습 니 다.이러한 장점 은 포장 에 유리 하고 호출 자 는 호출 할 때 어떤 인 자 를 전달 해 야 하 는 지 명확 하 게 알 수 있다 는 것 이다.예 를 들 어 다음 방법 은 Math.Max 방법 을 강력 한 유형의 의뢰 로 추출 합 니 다.

var tArgs = new Type[] { typeof(int), typeof(int) };
var maxMethod = typeof(Math).GetMethod("Max", tArgs);
var strongTypeDelegate = (Func<int, int, int>)Delegate
  .CreateDelegate(typeof(Func<int, int, int>), null, maxMethod);
Console.WriteLine("3   5       :{0}", strongTypeDelegate(3, 5)); //   :5
이 기술 은 속성 에 도 적용 되 며,강 한 유형의 Getter 와 Setter 를 얻 을 수 있 습 니 다.예시:

var theProperty = typeof(MyClass).GetProperty("MyIntProperty");

//     Getter
var theGetter = theProperty.GetGetMethod();
var strongTypeGetter = (Func<MyClass, int>)Delegate
  .CreateDelegate(typeof(Func<MyClass, int>), theGetter);
var intVal = strongTypeGetter(target); //    :target.MyIntProperty

//     Setter
var theSetter = theProperty.GetSetMethod();
var strongTypeSetter = (Action<MyClass, int>)Delegate
  .CreateDelegate(typeof(Action<MyClass, int>), theSetter);
strongTypeSetter(target, 5); //    :target.MyIntProperty = 5
사용자 정의 기능 반사 가 져 오기
다음은 네 가지 흔히 볼 수 있 는 장면 의 예 이다.
예 를 들 어 하나의 클래스 에 사용자 정의 특성(예 를 들 어 MyAtrribute)이 표 시 된 속성 을 찾 습 니 다.

var props = type
  .GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
  .Where(prop =>Attribute.IsDefined(prop, typeof(MyAttribute)));
예시 2,어떤 속성의 모든 사용자 정의 특성 을 찾 아 라.

var attributes = typeof(t).GetProperty("Name").GetCustomAttributes(false);
예제 3.프로그램 집합 에 사용자 정의 특성 이 표 시 된 모든 종 류 를 찾 아 라.

static IEnumerable<Type> GetTypesWithAttribute(Assembly assembly)
{
  foreach(Type type inassembly.GetTypes())
  {
    if (type.GetCustomAttributes(typeof(MyAttribute), true).Length > 0)
    {
      yield return type;
    }
  }
}
예제 4,실행 할 때 사용자 정의 기능 의 값 을 읽 습 니 다.

public static class AttributeExtensions
{
  public static TValue GetAttribute<TAttribute, TValue>(
    this Type type,
    string MemberName,
    Func<TAttribute, TValue> valueSelector,
    bool inherit = false)
    where TAttribute : Attribute
  {
    var att = type.GetMember(MemberName).FirstOrDefault()
      .GetCustomAttributes(typeof(TAttribute), inherit)
      .FirstOrDefault() as TAttribute;
    if (att != null)
    {
      return valueSelector(att);
    }
    return default;
  }
}

//   :

class Program
{
  static void Main()
  {
    //    MyClass    MyMethod     Description     
    var description = typeof(MyClass)
      .GetAttribute("MyMethod", (DescriptionAttribute d) => d.Description);
    Console.WriteLine(description); //   :Hello
  }
}
public class MyClass
{
  [Description("Hello")]
  public void MyMethod() { }
}
동적 실례 화 인터페이스의 모든 실현 클래스(플러그 인 활성화)
반 사 를 통 해 특정한 인터페이스의 모든 실현 류 를 동적 으로 예화 시 키 고 시스템 의 플러그 인 개발 을 실현 하 는 데 자주 사용 된다.예 를 들 어 프로그램 이 시 작 될 때 지정 한 폴 더(예 를 들 어 Plugins)의 dll 파일 을 읽 고 dll 의 모든 인터페이스 클래스 를 반사 적 으로 가 져 오 며 적당 할 때 이 를 예 로 들 수 있 습 니 다.대체로 다음 과 같다.

interface IPlugin
{
  string Description { get; }
  void DoWork();
}
독립 dll 에 있 는 클래스:

class HelloPlugin : IPlugin
{
  public string Description => "A plugin that says Hello";
  public void DoWork()
  {
    Console.WriteLine("Hello");
  }
}
시스템 이 시 작 될 때 이 dll 을 동적 으로 불 러 와 IPlugin 인 터 페 이 스 를 실현 하 는 모든 종류의 정 보 를 읽 고 이 를 예 로 들 수 있 습 니 다.

public IEnumerable<IPlugin> InstantiatePlugins(string directory)
{
  var assemblyNames = Directory.GetFiles(directory, "*.addin.dll")
    .Select(name => new FileInfo(name).FullName).ToArray();

  foreach (var fileName assemblyNames)
    AppDomain.CurrentDomain.Load(File.ReadAllBytes(fileName));

  var assemblies = assemblyNames.Select(System.Reflection.Assembly.LoadFile);
  var typesInAssembly = assemblies.SelectMany(asm =>asm.GetTypes());
  var pluginTypes = typesInAssembly.Where(type => typeof (IPlugin).IsAssignableFrom(type));

  return pluginTypes.Select(Activator.CreateInstance).Cast<IPlugin>();
}
일반적인 인 스 턴 스 의 일반적인 매개 변 수 를 검사 합 니 다.
앞에서 구조 범 형 과 구상 범 형 을 언급 했 는데 여기 서 설명 하 자.대부분 우리 가 말 하 는 범 형 은 구조 범 형 을 말 하 는데 가끔 은 구상 범 형 이 라 고도 부른다.예 를 들 어List<int>는 하나의 구조 범 형 이다.왜냐하면 그것 은 new 를 통 해 예화 할 수 있 기 때문이다.이에 따라List<> 범 형 은 비구 조 범 형 이 고 때로는 개방 범 형 이 라 고도 불 리 며 실례 화 되 어 서 는 안 된다.개방 범 형 은 반 사 를 통 해 임의의 구상 범 형 으로 전환 할 수 있다 는 점 은 앞에서 예시 가 있다.
만약 에 지금 범 형 인 스 턴 스 가 있다 면 특정한 수요 에서 우 리 는 이 범 형 인 스 턴 스 를 구축 하 는 데 어떤 범 형 인 자 를 사용 해 야 하 는 지 알 고 싶 습 니 다.예 를 들 어 누군가가 List<T> 일반적인 인 스 턴 스 를 만 들 고 이 를 매개 변수 로 우리 에 게 전달 하 는 방법 이다.

var myList = newList<int>();
ShowGenericArguments(myList);
우리 의 방법 서명 은 다음 과 같다.

public void ShowGenericArguments(object o)
이때 이 방법의 작성 자로 서 우 리 는 이 o 대상 이 구체 적 으로 어떤 유형의 일반적인 매개 변수 로 구축 되 었 는 지 모른다.반 사 를 통 해 우 리 는 범 형 실례 의 많은 정 보 를 얻 을 수 있다.그 중에서 가장 간단 한 것 은 하나의 유형 이 범 형 인지 아 닌 지 를 판단 하 는 것 이다.

public void ShowGenericArguments(object o)
{
  if (o == null) return;
  Type t =o.GetType();
  if (!t.IsGenericType) return;
  ...
}
List<> 자체 도 범 형 이기 때문에 위의 판단 이 엄밀 하지 않 기 때문에 우 리 는 대상 이 하나의 구조 범 형List<int>인지 알 아야 한다.그리고 Type 류 는 유용 한 속성 도 제공 합 니 다.

typeof(List<>).IsGenericType // true
typeof(List<>).IsGenericTypeDefinition // true
typeof(List<>).IsConstructedGenericType// false

typeof(List<int>).IsGenericType // true
typeof(List<int>).IsGenericTypeDefinition // false
typeof(List<int>).IsConstructedGenericType// true
IsConstructedGenericType IsGenericTypeDefinition는 각각 특정한 범 형 이 구조 범 형 과 비 구조 범 형 인지 판단 하 는 데 사용 된다.
Type 의 GetGenericArguments() 방법 을 결합 하면 특정한 범 형 인 스 턴 스 가 어떤 범 형 매개 변수 로 구축 되 었 는 지 쉽게 알 수 있다.예 를 들 어:

static void ShowGenericArguments(object o)
{
  if (o == null) return;
  Type t = o.GetType();
  if (!t.IsConstructedGenericType) return;
  foreach (Type genericTypeArgument in t.GetGenericArguments())
    Console.WriteLine(genericTypeArgument.Name);
}
이상 은 C\#의 반사 에 대한 상세 한 내용 을 깊이 이해 하고 c\#반사 에 관 한 자 료 는 우리 의 다른 관련 글 을 주목 하 세 요!

좋은 웹페이지 즐겨찾기