1. 객체를 작성하는 방법
입문의 첫 편으로 가장 간단한 창설 대상부터 시작하자.
먼저 c#에서 어떤 유형의 대상을 어떻게 만드는지 돌이켜 보면 가장 기초적인 것은 바로 new 키워드를 사용하는 것이다. 예를 들어 new object()이다. 물론 이 방식도 단점이 있다. 바로 번역할 때 유형을 명확하게 알아야 한다는 것이다.
만약 컴파일할 때 유형을 모르지만 실행할 때 구체적인 유형을 알면 어떻게 도와줍니까?
첫 번째 사진은 당연히 Activator를 사용합니다.CreateInstance 메서드(예:
static void Main(string [] args)
{
Type type = typeof (object );
Console .WriteLine(Create(type));
}
static object Create(Type type)
{
return Activator .CreateInstance(type);
}
또는 범용 재부팅을 사용합니다.
static void Main(string [] args)
{
Console .WriteLine(Create<object >());
}
static T Create<T>()
{
return Activator .CreateInstance<T>();
}
물론, 이곳의 type이나 T는 기본 구조 함수가 있어야 합니다. 그렇지 않으면 실행 중 오류가 발생할 수 있습니다.
두 번째 방법은 다음과 같은 일반 구속 new()를 사용합니다.
static void Main(string [] args)
{
Console .WriteLine(Create<object >());
}
static T Create<T>() where T : new ()
{
return new T();
}
T가 기본 구조 함수를 갖추지 못하면 컴파일할 때 오류가 발생합니다.
물론 세 번째 방법인 반사도 있다. 예를 들어 다음과 같다.
static void Main(string [] args)
{
Console .WriteLine(Create(typeof (object )));
}
static object Create(Type type)
{
var ctor = type.GetConstructor(Type .EmptyTypes);
return ctor.Invoke(new object [0]);
}
2. 성능 비교
물론 이 세 가지 방식의 성능은 각기 다르다. 세 번째 반사 방식은 최악의 성능을 가진다. 일반적인 new 제약이 T가 인용 유형일 때 컴파일러는 new T()를Activator로 자동으로 변환한다.CreateInstance
의 호출은 T가 값 유형인 경우 효율적이고 높습니다.Activator.CreateInstance의 두 가지 재부팅의 효율은 호출 횟수와 비범용판의 캐시 명중률에 달려 있다. 호출 횟수가 충분할 때 대량으로 명중된 상황에서 비범용판은 상대적으로 높은 효율을 가진다. 즉, 비범용판은 캐시가 명중되지 않은 상황에서 비교적 높은 대가를 필요로 하지만 캐시가 명중된 상황에서 상대적으로 적은 대가를 받을 수 있다. 테스트 코드는 다음과 같다.Type t = typeof (object );
var sw = new Stopwatch ();
int count = 1; // 10, 100, 1000
sw.Start();
for (int i = 0; i < count; i++)
Activator .CreateInstance(t);
Console .WriteLine("Method:CreateInstance, Count:{0}, Tick:{1}" ,
count.ToString(), sw.ElapsedTicks.ToString());
sw.Reset();
sw.Start();
for (int i = 0; i < count; i++)
Activator .CreateInstance<object >();
Console .WriteLine("Method:CreateInstance<T>, Count:{0}, Tick:{1}" ,
count.ToString(), sw.ElapsedTicks.ToString());
범용 버전이 아닌 캐시를 피하기 위해 매번 다시 실행해야 합니다. 실행 결과는 다음과 같습니다. Method:CreateInstance, Count:1, Tick:1399Method:CreateInstance, Count:1, Tick:42 Method:CreateInstance, Count:10, Tick:3009Method:CreateInstance, Count:10, Tick:112 Method:CreateInstance, Count:100, Tick:3343Method:CreateInstance, Count:100, Tick:820 Method:CreateInstance, Count:1000, Tick:4092Method:CreateInstance, Count:1000, Tick:7989 범형판의 대가가 상당히 안정적이라는 것을 쉽게 알 수 있다. 첫 번째를 제외하고 매번 호출되는 대가가 약 8-10개의 Tick이고 범형판이 아닌 대가의 변화가 크다. 100번 호출된 대가 중 10번 호출된 대가가 90%(사실상 전 2회)를 차지했지만 이후의 대가가 현저히 떨어졌지만 직접 new object()에 비해 대가가 비교적 크다. 3. IL 소개 IL 로 돌아가 Emit 을 사용하면 성능을 더 향상시킬 수 있습니까?그럼 우선 Emit으로 대상을 만드는 방법을 써야 하는데 어떻게 쓰지 않을까요? 좋아, 이제 도구가 등장할 때야. 우선 c#컴파일러야. 예를 들어 다음과 같은 방법을 써.static object MyCreateInstance()
{
return new object ();
}
그리고 컴파일링(본문의 모든 컴파일링은Release 방식을 사용)을 하면 dll 또는exe 파일을 얻을 수 있다. 그리고reflector가 등장할 차례이다. 이 파일을 열고 클래스와 방법을 찾아 역컴파일된 언어를 IL로 선택하면 다음과 같은 내용을 얻을 수 있다..method private hidebysig static object MyCreateInstance() cil managed
{
.maxstack 8
L_0000: newobj instance void [mscorlib]System.Object::.ctor()
L_0005: ret
}
아니면 손댈 수가 없는 것 같아?알겠습니다. 여기. 에서 리플렉터 플러그인을 다운로드할 수 있습니다. 예를 들어 ReflectionEmitLanguage 플러그인을 불러오면 리플렉터가 여러 언어를 선택할 수 있습니다. 리플렉션.Emit, 언어를 선택하면 다음을 볼 수 있습니다.public MethodBuilder BuildMethodMyCreateInstance(TypeBuilder type)
{
// Declaring method builder
// Method attributes
System.Reflection.MethodAttributes methodAttributes =
System.Reflection.MethodAttributes .Private
| System.Reflection.MethodAttributes .HideBySig
| System.Reflection.MethodAttributes .Static;
MethodBuilder method = type.DefineMethod("MyCreateInstance" , methodAttributes);
// Preparing Reflection instances
ConstructorInfo ctor1 = typeof (Object ).GetConstructor(
BindingFlags .Instance | BindingFlags .Public | BindingFlags .NonPublic,
null ,
new Type []{
},
null
);
// Setting return type
method.SetReturnType(typeof (Object ));
// Adding parameters
ILGenerator gen = method.GetILGenerator();
// Writing body
gen.Emit(OpCodes .Newobj, ctor1);
gen.Emit(OpCodes .Ret);
// finished
return method;
}
하지만 이것은 유형을 생성하는 방법입니다. 지금 필요한 것은 단지 하나의 방법을 생성하는 것입니다. 조금만 개조해 봅시다.static Func <object > BuildMethodMyCreateInstance()
{
DynamicMethod dm = new DynamicMethod (string .Empty, typeof (object ), Type .EmptyTypes);
var gen = dm.GetILGenerator();
gen.Emit(OpCodes .Newobj, typeof (object ).GetConstructor(Type .EmptyTypes));
gen.Emit(OpCodes .Ret);
return (Func <object >)dm.CreateDelegate(typeof (Func <object >));
}
이렇게 해서 아까 MyCreateInstance 방법의 내용을 Func로 포장하여 정상적으로 작동할 수 있는지 확인합니다.Func <object > func = BuildMethodMyCreateInstance();
Console .WriteLine(func());
실행 결과를 보려면 다음과 같이 하십시오. System.Object 작업은 괜찮았지만 문제가 생겼다. 우리가 원하는 것은 Object만 만들 수 있는 간단한 방법이 아니라 전송할 수 있는 유형과 이 유형의 대상을 만들 수 있는 방법이 필요하다. 그래서 더욱 개선해야 한다.static Func <object > BuildMethodMyCreateInstance(Type type)
{
DynamicMethod dm = new DynamicMethod (string .Empty, typeof (object ), Type .EmptyTypes);
var gen = dm.GetILGenerator();
gen.Emit(OpCodes .Newobj, type.GetConstructor(Type .EmptyTypes));
gen.Emit(OpCodes .Ret);
return (Func <object >)dm.CreateDelegate(typeof (Func <object >));
}
매개 변수를 추가하여 조금 고쳤으니 테스트를 봅시다.Func <object > func = BuildMethodMyCreateInstance(typeof (object ));
Console .WriteLine(func());
출력: System.Object 좋아, 이제 우리 한층 더 테스트해 보자. 4. 성능 비교 (2) 테스트 방법을 수정하여 Emit과 직접 초기 바인딩을 포함하는 성능 테스트를 수행합니다.Type t = typeof (object );
var sw = new Stopwatch ();
int count = 1; // 10, 100, 1000
sw.Reset();
sw.Start();
for (int i = 0; i < count; i++)
Activator .CreateInstance(t);
Console .WriteLine("Method:CreateInstance, Count:{0}, Tick:{1}" ,
count.ToString(), sw.ElapsedTicks.ToString());
sw.Reset();
sw.Start();
for (int i = 0; i < count; i++)
Activator .CreateInstance<object >();
Console .WriteLine("Method:CreateInstance<T>, Count:{0}, Tick:{1}" ,
count.ToString(), sw.ElapsedTicks.ToString());
sw.Reset();
sw.Start();
Func <object > func = BuildMethodMyCreateInstance(t);
for (int i = 0; i < count; i++)
func();
Console .WriteLine("Method:MyCreateInstance, Count:{0}, Tick:{1}" ,
count.ToString(), sw.ElapsedTicks.ToString());
sw.Reset();
sw.Start();
for (int i = 0; i < count; i++)
new object ();
Console .WriteLine("Method:new object(), Count:{0}, Tick:{1}" ,
count.ToString(), sw.ElapsedTicks.ToString());
테스트 결과를 살펴보겠습니다. Method:CreateInstance, Count:1, Tick:1449Method:CreateInstance, Count:1, Tick:56Method:MyCreateInstance, Count:1, Tick:11288Method:new object(), Count:1, Tick:10 와, 단 한 번의 성능으로 말하자면CreateInstance를 순순히 사용하십시오. (또는 일반적인 new () 제약을 사용하십시오.) Method:CreateInstance, Count:10, Tick:3046Method:CreateInstance, Count:10, Tick:113Method:MyCreateInstance, Count:10, Tick:10802Method:new object(), Count:10, Tick:32 10번의 Emit 결과는 여전히 차이가 많지 않다(Emit의 시간이 줄어드는 것은 오차에 속한다) Method:CreateInstance, Count:100, Tick:3211Method:CreateInstance, Count:100, Tick:811Method:MyCreateInstance, Count:100, Tick:9442Method:new object(), Count:100, Tick:13 100번의 Emit 결과는 여전히 안정적이어서 거의 증가하지 않았다 Method:CreateInstance, Count:1000, Tick:3964Method:CreateInstance, Count:1000, Tick:8031Method:MyCreateInstance, Count:1000, Tick:10982Method:new object(), Count:1000, Tick:90 1000번의 Emit 결과는 여전히 안정적이다(오차가 소모된 것보다 더 많다).하지만Create Instance보다 못하다.그러면 연장전을 한 번 하자. Method:CreateInstance, Count:10000, Tick:13396Method:CreateInstance, Count:10000, Tick:81018Method:MyCreateInstance, Count:10000, Tick:10706Method:new object(), Count:10000, Tick:564 10000번의 실행이 드디어CreateInstance를 넘어섰습니다. Method:CreateInstance, Count:100000, Tick:102373Method:CreateInstance, Count:100000, Tick:768419Method:MyCreateInstance, Count:100000, Tick:15689Method:new object(), Count:100000, Tick:4778 100000회 실행된 상황에서 Emit의 장점은 완전히 드러났다. 성능은CreateInstance의 6배가 넘고CreateInstance의 50배에 가깝다.또한 첫 번째 Emit의 대가를 빼면 new object()에 가까운 대가를 받게 됩니다.(15689-약 10000의 첫 대가, 받은 이후 약 5000여 Tick, 직접 new object() 5000번의 Tick과 매우 가깝다) 따라서 대상을 대량으로 만들어야 한다면 가능한 한 Emit 또는 조기 귀속 방식을 사용해야 한다.몇 번만 만들면CreateInstance를 선택하거나 조기 귀속을 선택해야 합니다. 5.승급 진짜 좋은 줄 알았어요?앞서 언급한 IL에 대해 특별한 처리 값 유형이 필요하다는 것을 기억하십니까?이 테스트 용례를 봅시다. 처리되지 않은 예외: System.ArgumentNullException: 값은 비워 둘 수 없습니다.매개변수 이름: con은 System에 있습니다.Reflection.Emit.DynamicILGenerator.Emit(OpCode opcode, ConstructorInfo con) Constructor Info가 실례를 가져오지 않았기 때문에 오류가 발생했습니다. 왜?값 유형에는 일반적으로 기본 구조 함수가 없기 때문이다. (거의 모든 고급 언어는 값 유형의 기본 구조 함수를 쓸 수 없지만 IL은 막지 않는다.) 그럼 평소에:static void X()
{
int x = new int ();
Console .WriteLine(x);
}
어떻게 된 거예요?Reflector를 계속 사용해 보십시오..method private hidebysig static void X() cil managed
{
.maxstack 1
.locals init (
[0] int32 x)
L_0000: ldc.i4.0
L_0001: stloc.0
L_0002: ldloc.0
L_0003: call void [mscorlib]System.Console::WriteLine(int32)
L_0008: ret
}
c#컴파일러가 직접int x = new int ();
번역하다int x = 0;
그럼 다른 값으로 바꾸면 어때요?예를 들면 다음과 같습니다.static void X()
{
int ? x = null ;
Console .WriteLine(x);
}
c#컴파일러는 다음과 같이 번역됩니다..method private hidebysig static void X() cil managed
{
.maxstack 1
.locals init (
[0] valuetype [mscorlib]System.Nullable`1<int32> x)
L_0000: ldloca.s x
L_0002: initobj [mscorlib]System.Nullable`1<int32>
L_0008: ldloc.0
L_0009: box [mscorlib]System.Nullable`1<int32>
L_000e: call void [mscorlib]System.Console::WriteLine(object )
L_0013: ret
}
인트 처리하는 거 보이시나요?x = null;c# 컴파일러는 다음을 사용합니다.ldloca.s x
initobj [mscorlib]System.Nullable`1<int32>
이 두 마디,ldloca.s는ldloca의 짧은 형식입니다.ldloca는 Load Local variable Address라는 뜻으로 x변수를 불러오는 주소입니다.그 다음에 initobj라는 조작부호를 사용했다. 이것이 바로.net의 고급 언어는 모두 값 형식을 정의할 필요가 없는 이유입니다. initobj는 CLR에서 하나의 값 형식을 초기화하는 조작부호입니다. 따라서 이전의 방법을 한층 더 개선하고 값 유형을 지원할 수 있다. 물론 원래의 방법을 이용하여 상응하는 도움을 받아 아래의 개량판을 얻을 수 있다.static Func <object > BuildMethodMyCreateInstance(Type type)
{
DynamicMethod dm = new DynamicMethod (string .Empty, typeof (object ), Type .EmptyTypes);
var gen = dm.GetILGenerator();
if (type.IsValueType)
{
gen.DeclareLocal(type);
gen.Emit(OpCodes .Ldloca_S, 0);
gen.Emit(OpCodes .Initobj, type);
gen.Emit(OpCodes .Ldloc_0);
gen.Emit(OpCodes .Box, type);
}
else
{
gen.Emit(OpCodes .Newobj, type.GetConstructor(Type .EmptyTypes));
}
gen.Emit(OpCodes .Ret);
return (Func <object >)dm.CreateDelegate(typeof (Func <object >));
}
현재 이 방법은 값 형식과 인용 형식의 상황을 동시에 처리할 수 있다.
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
List 컬렉션 객체에서 서로 다른 속성 크기로 정렬된 인스턴스
인스턴스는 다음과 같습니다.
테스트: 출력 결과는 다음과 같습니다.
두 번째 방법은 컬렉션에 따라.sort 재부팅 방법(예: 마스터 클래스에서 이렇게 작성하면 됩니다.
출력 결과는 다음과 같습니다.
전자의 코드 구조...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.