.NET 에서 대상 을 만 드 는 몇 가지 방식 과 비교
저 는 사용 하 는 간이 도와 유연성 에 따라 아래 의 순 서 를 매 겼 습 니 다.예 를 들 어 Source Generators 등 다른 반사 방식 도 있 을 수 있 습 니 다.본 고 는 다음 과 같은 몇 가지 만 테스트 했 습 니 다.
Type typeToCreate = typeof(Employee);
ConstructorInfo ctor = typeToCreate.GetConstructor(System.Type.EmptyTypes);
Employee employee = ctor.Invoke(null) as Employee;
첫 번 째 단 계 는 type of()를 통 해 대상 의 유형 을 가 져 오 는 것 입 니 다.GetType 방식 을 통 해 GetConstructor 방법 을 호출 하여 System.Type.Empty Types 인 자 를 전달 할 수 있 습 니 다.실제로 빈 배열(new Type[0])입 니 다.Constructor Info 대상 으로 돌아 간 다음 Invoke 방법 을 호출 하면 Employee 대상 을 되 돌려 줍 니 다.이것 은 반 사 를 사용 하 는 가장 간단 하고 유연 한 방법 중 하나 이다.유사 한 방법 으로 대상 의 방법,인터페이스 와 속성 등 을 호출 할 수 있 지만 이것 도 가장 느 린 반사 방법 중 하나 이기 때문이다.
Activator.CreateInstance 사용 하기
대상 을 만 들 필요 가 있다 면.NET Framework 와.NET Core 에 이 를 위 한 정적 클래스 가 있 습 니 다.System.activator 는 사용 방법 이 매우 간단 하고 범 형 을 사용 할 수 있 으 며 다른 매개 변 수 를 입력 할 수 있 습 니 다.
Employee employee = Activator.CreateInstance<Employee>();
Microsoft.Extensions.Dependency Injection 사용 하기다음은.NET Core 에 익숙 한 IOC 용기,Microsoft.Extensions.Dependency Injection 입 니 다.용기 에 유형 을 등록 한 후에 저 희 는 IServiceProvider 를 사용 하여 대상 을 얻 습 니 다.여기 서 저 는 Transient 의 생명 주 기 를 사용 하여 매번 새로운 대상 을 만 들 도록 보장 합 니 다.
IServiceCollection services = new ServiceCollection();
services.AddTransient<Employee>();
IServiceProvider provider = services.BuildServiceProvider();
Employee employee = provider.GetService<Employee>();
NatashaNatasha 는 Roslyn 이 개발 한 동적 프로그램 집합 을 기반 으로 라 이브 러 리 를 구축 하고 직관 적 이 고 유창 한 Fluent API 디자인 으로 roslyn 의 강력 한 부 여 를 통 해 프로그램 이 실 행 될 때 코드 를 만 들 수 있 습 니 다.프로그램 집합,클래스,구조 체,매 거 진,인터페이스,방법 등 을 포함 하여 새로운 기능 과 모듈 을 추가 할 수 있 습 니 다.여기 서 저 희 는 NInstance 로 대상 을 만 들 수 있 습 니 다.
// Natasha
NatashaInitializer.Initialize();
Employee employee = Natasha.CSharp.NInstance.Creator<Employee>().Invoke();
표현 식 표현 식 사용표현 식 Expression 도 존재 한 지 오래 되 었 습 니 다.System.Linq.Expressions 네 임 스페이스 에서 여러 가지 기능(LINQ)과 라 이브 러 리(EF Core)가 부족 하거나 부족 한 부분 입 니 다.여러 가지 측면 에서 반사 와 유사 합 니 다.실행 할 때 코드 를 조작 할 수 있 기 때 문 입 니 다.
NewExpression constructorExpression = Expression.New(typeof(Employee));
Expression<Func<Employee>> lambdaExpression = Expression.Lambda<Func<Employee>>(constructorExpression);
Func<Employee> func = lambdaExpression.Compile();
Employee employee = func();
표현 식 은 성명 식 코드 에 사용 할 고급 언어 를 제공 합 니 다.앞의 두 줄 에서 만 든 표현 식 은()=>new Employee()와 같 습 니 다.그리고 Compile 방법 을 호출 하여 Func<>의 의뢰 를 받 았 습 니 다.마지막 으로 이 Func 를 호출 하여 Employee 대상 을 되 돌려 줍 니 다.Emit 사용 하기
Emit 는 주로 System.Reflection.Emit 네 임 스페이스 에서 프로그램 에서 IL(중간 코드)코드 를 직접 만 들 수 있 습 니 다.IL 코드 는 컴 파일 러 가 컴 파일 러 에서 출력 하 는'위조 어 셈 블 리 코드',즉 컴 파일 된 dll 을 말 합 니 다.프로그램 이 실 행 될 때.NET CLR 의 JIT 컴 파일 러 는 이 IL 명령 을 실제 어 셈 블 리 코드 로 변환 합 니 다.
다음은 실행 할 때 새로운 방법 을 만들어 야 합 니 다.간단 합 니 다.인자 가 없습니다.Employee 대상 을 만 들 고 바로 돌아 갑 니 다.
Employee DynamicMethod()
{
return new Employee();
}
System.Reflection.Emit.DynamicMethod 동적 생 성 방법 을 사용 합 니 다.
DynamicMethod dynamic = new("DynamicMethod", typeof(Employee), null, typeof(ReflectionBenchmarks).Module, false);
DynamicMethod 대상 을 만 들 고 방법 명,반환 값,방법의 매개 변수 와 있 는 모듈 을 지 정 했 습 니 다.마지막 매개 변수 false 는 JIT 가시 성 검 사 를 뛰 어 넘 지 않 음 을 표시 합 니 다.우 리 는 지금 서명 할 방법 이 있 지만 방법 체 가 없습니다.방법 체 를 채 워 야 합 니 다.여 기 는 C\#코드 를 IL 코드 로 변환 해 야 합 니 다.사실은 이 렇 습 니 다.
IL_0000: newobj instance void Employee::.ctor()
IL_0005: ret
이 사이트 에 접근 할 수 있 습 니 다.C\#를 IL 코드 로 편리 하 게 변환 할 수 있 습 니 다.https://sharplab.io/그리고 ILGenerator 를 사용 하여 IL 코드 를 조작 한 다음 Func<>의뢰 를 만 들 고 마지막 으로 이 의뢰 를 실행 하여 Employee 대상 을 되 돌려 줍 니 다.
ConstructorInfor ctor = typeToCreate.GetConstructor(System.Type.EmptyTypes);
ILGenerator il = createHeadersMethod.GetILGenerator();
il.Emit(OpCodes.Newobj, Ctor);
il.Emit(OpCodes.Ret);
Func<Employee> emitActivator = dynamic.CreateDelegate(typeof(Func<Employee>)) as Func<Employee>;
Employee employee = emitActivator();
기준 테스트위 에서 저 는 몇 가지 대상 을 만 드 는 방식 을 소 개 했 습 니 다.지금부터 저 는 BenchmarkDotNet 을 사용 하여 기준 테스트 를 하기 시 작 했 습 니 다.저도 new Employee()를 직접 만 드 는 방식 을 테스트 목록 에 추가 하고 이 를'기선'으로 삼 아 다른 모든 방법 을 비교 하 는 동시에 저 는 몇 가지 방법의 예열 작업 을 구조 함수 에 넣 어 실 행 했 습 니 다.최종 코드 는 다음 과 같 습 니 다.
using BenchmarkDotNet.Attributes;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
namespace ReflectionBenchConsoleApp
{
public class Employee { }
public class ReflectionBenchmarks
{
private readonly ConstructorInfo _ctor;
private readonly IServiceProvider _provider;
private readonly Func<Employee> _expressionActivator;
private readonly Func<Employee> _emitActivator;
private readonly Func<Employee> _natashaActivator;
public ReflectionBenchmarks()
{
_ctor = typeof(Employee).GetConstructor(Type.EmptyTypes);
_provider = new ServiceCollection().AddTransient<Employee>().BuildServiceProvider();
NatashaInitializer.Initialize();
_natashaActivator = Natasha.CSharp.NInstance.Creator<Employee>();
_expressionActivator = Expression.Lambda<Func<Employee>>(Expression.New(typeof(Employee))).Compile();
DynamicMethod dynamic = new("DynamicMethod", typeof(Employee), null, typeof(ReflectionBenchmarks).Module, false);
ILGenerator il = dynamic.GetILGenerator();
il.Emit(OpCodes.Newobj, typeof(Employee).GetConstructor(System.Type.EmptyTypes));
il.Emit(OpCodes.Ret);
_emitActivator = dynamic.CreateDelegate(typeof(Func<Employee>)) as Func<Employee>;
}
[Benchmark(Baseline = true)]
public Employee UseNew() => new Employee();
[Benchmark]
public Employee UseReflection() => _ctor.Invoke(null) as Employee;
[Benchmark]
public Employee UseActivator() => Activator.CreateInstance<Employee>();
[Benchmark]
public Employee UseDependencyInjection() => _provider.GetRequiredService<Employee>();
[Benchmark]
public Employee UseNatasha() => _natashaActivator();
[Benchmark]
public Employee UseExpression() => _expressionActivator();
[Benchmark]
public Employee UseEmit() => _emitActivator();
}
}
다음은 Program.cs 를 수정 합 니 다.Release 모드 에서 테스트 를 실행 해 야 합 니 다.
using BenchmarkDotNet.Running;
namespace ReflectionBenchConsoleApp
{
public class Program
{
public static void Main(string[] args)
{
var sumary = BenchmarkRunner.Run<ReflectionBenchmarks>();
}
}
}
테스트 결과이곳 의 환경 은.NET 6 preview 5 입 니 다.표준 반 사 를 사용 하 는 Invoke()방법 은 간단 하지만 가장 느 린 것 입 니 다.Activator.Create Instance()와 Microsoft.Extensions.Dependency Injection()을 사용 하 는 시간 차 가 많 지 않 습 니 다.시간 은 직접 new 가 만 든 16 배 입 니 다.표현 식 Expression 을 사용 하 는 것 이 가장 우수 합 니 다.Natasha 는 정말 흑 과학기술 입 니 다.Emit 를 사용 하 는 것 보다 조금 빠 르 고,Emit 를 사용 하 는 것 은 직접 new 를 만 드 는 시간의 1.8 배 에 달 합 니 다.여러 가지 방식 간 의 차 이 를 발견 해 야 하지만 주의해 야 할 것 은 여기 가 ns 나 초 이 고 1 나 초 는 1 초의 10 억 분 의 1 이다.
여기 서 몇 가지 대상 을 만 드 는 방법 을 간단하게 비교 하 였 습 니 다.테스트 결과 가 특별히 정확 하지 않 을 수도 있 습 니 다.관심 있 는 것 은.net framework 에서 테스트 할 수 있 습 니 다.도움 이 되 기 를 바 랍 니 다!
관련 링크
https://andrewlock.net/benchmarking-4-reflection-methods-for-calling-a-constructor-in-dotnet/
https://github.com/dotnetcore/Natasha
.NET 에서 대상 을 만 드 는 몇 가지 방식 과 비교 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은.NET 생 성 대상 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 시기 바 랍 니 다.앞으로 많은 응원 바 랍 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Visual Studio 2017에서 SQLite를 사용한 Windows Forms 앱 개발Visual Studio 2017에서 SQLite를 사용하여 Windows Forms 앱을 개발해 보았습니다. 아직 서버 탐색기나 TableAdaptor를 사용한 GUI에서의 개발에는 대응하지 않는 것 같습니다. 이...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.