대학에서의 임무는 Reflection.Emit을 사용하여 간단한 프록시 생성기/인터셉터 메커니즘을 구현하는 것이 었습니다. 다음 프로그램을 생각해 냈습니다.Reflection.Emit을 통한 프록시 생성은 디버깅으로 시작될 때만 작동합니다.
디버그 모드 [F5]
(디버그 -> 디버깅 시작)에서 Visual Studio 내에서 정상적으로 작동하지만 디버깅하지 않고 시작했을 때 대부분의 경우 디버그 [Ctrl + F5]
(디버그 -> 디버깅하지 않고 시작)과 충돌합니다.
이 두 모드의 차이점은 무엇입니까? (은 디버그 <> 릴리스 모드 참조). 이 문제는 여러 컴퓨터/설정 (Win XP SP3 32 비트 및 64 비트, Windows 7 32 비트)에서 발생합니다.
Click pastebin.
// The proxy generator; I assume that the error is buried along the lines emitting the IL code
public static class ProxyGenerator
{
public static T Create<T>(object obj, IInterception interception)
{
Type type = obj.GetType();
TypeBuilder proxy = DefineProxy(type);
FieldBuilder wrappedField = DefinePrivateField(proxy, "wrappedObject", type);
FieldBuilder interceptionField = DefinePrivateField(proxy, "interception", interception.GetType());
DefineConstructor(proxy, wrappedField, interceptionField);
DefineInterfaceMethods(type, proxy, wrappedField, interceptionField);
return (T) Activator.CreateInstance(proxy.CreateType(), obj, interception);
}
private static TypeBuilder DefineProxy(Type type)
{
var assemblyName = new AssemblyName {Name = "GeneratedProxyAssembly"};
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("GeneratedProxyModule");
return moduleBuilder.DefineType(
type.Name + "Proxy",
type.Attributes,
typeof (object),
type.GetInterfaces());
}
private static FieldBuilder DefinePrivateField(TypeBuilder typeBuilder, string fieldName, Type fieldType)
{
return typeBuilder.DefineField(fieldName, fieldType, FieldAttributes.Private);
}
private static void DefineConstructor(TypeBuilder typeBuilder, params FieldBuilder[] parameters)
{
ConstructorBuilder ctor = typeBuilder.DefineConstructor(
MethodAttributes.Public, CallingConventions.Standard, parameters.Select(f => f.FieldType).ToArray());
// Emit constructor
ILGenerator g = ctor.GetILGenerator();
// Load "this" pointer and call base constructor
g.Emit(OpCodes.Ldarg_0);
g.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0]));
// Store parameters in private fields
for (int i = 0; i < parameters.Length; i++)
{
// Load "this" pointer and parameter and store paramater in private field
g.Emit(OpCodes.Ldarg_0);
g.Emit(OpCodes.Ldarg, i + 1);
g.Emit(OpCodes.Stfld, parameters[i]);
}
// Return
g.Emit(OpCodes.Ret);
}
private static void DefineInterfaceMethods(Type type, TypeBuilder proxy, FieldInfo wrappedField, FieldInfo interceptionField)
{
// Loop through all interface methods
foreach (MethodInfo interfaceMethod in type.GetInterfaces().SelectMany(i => i.GetMethods()))
{
MethodInfo method = type.GetMethod(interfaceMethod.Name);
MethodBuilder methodBuilder = proxy.DefineMethod(
method.Name,
method.Attributes,
method.ReturnType,
method.GetParameters().Select(p => p.ParameterType).ToArray());
// Emit method
ILGenerator g = methodBuilder.GetILGenerator();
// Intercept before
EmitMethodCallOnMember(g, interceptionField, "Before", false);
// Delegate method call
EmitMethodCallOnMember(g, wrappedField, method.Name, true);
// Intercept after
EmitMethodCallOnMember(g, interceptionField, "After", false);
// Return
g.Emit(OpCodes.Ret);
}
}
private static void EmitMethodCallOnMember(ILGenerator g, FieldInfo field, string methodName, bool delegateParameters)
{
// Load "this" pointer to get address of field
g.Emit(OpCodes.Ldarg_0);
g.Emit(OpCodes.Ldflda, field);
MethodInfo method = field.FieldType.GetMethod(methodName);
if (delegateParameters)
{
// Load method parameters
for (int i = 0; i < method.GetParameters().Length; i++)
{
g.Emit(OpCodes.Ldarg, i + 1);
}
}
// Emit call
g.Emit(OpCodes.Call, method);
}
}
// Some infrastructure
public interface IInterception
{
void Before();
void After();
}
public class LogInterception : IInterception
{
public void Before()
{
Console.WriteLine("Before ... ");
}
public void After()
{
Console.WriteLine("... After");
}
}
public interface ITest
{
string DoSomething(string s1, string s2);
}
public class Test : ITest
{
public string DoSomething(string s1, string s2)
{
Console.WriteLine("... doing something ...");
return s1 + s2;
}
}
// The test program, expected output is down below
internal class Program
{
internal static void Main(string[] args)
{
var test = new Test();
var proxy = ProxyGenerator.Create<ITest>(test, new LogInterception());
Console.WriteLine(test.DoSomething("Hello", " World"));
Console.WriteLine("----------------------------------------");
Console.WriteLine(proxy.DoSomething("Hello", " World"));
Console.ReadKey();
}
}
또 다른 질문 : 이러한 문제를 좁히는 가장 좋은 방법은 무엇입니까? 생성 된 어셈블리를 디스크에 저장하고 Reflector에 결과 dll을 열려고했지만 비어있는 것으로 보입니다.
위에서 언급했듯이 디버그 모드에서 시작할 때 프로그램이 작동하는 것으로 보이며 다음 출력을 인쇄합니다.
... doing something ...
Hello World
----------------------------------------
Before ...
... doing something ...
... After
Hello World
감사합니다.
어떤 예외가 있습니까? 디버그 및 릴리스 모드 모두에서 코드가 잘 작동합니다. – oxilumin
앞에서 언급했듯이 디버깅 모드 나 릴리즈 모드는 아니며 '디버깅 시작'및 '디버깅하지 않고 시작'이라고합니다. –
확인. 나는 그 문제를 발견했다고 생각한다. 이제는 디버그 모드에서 왜 작동하는지 정말 모르겠다. – oxilumin