2017-09-17 3 views
1

동적 클래스를 많이 사용하지 않고 방출하고 벽돌 벽에 머리를 대고 있습니다. 내가 원하는 것은 구조체와 다른 값 유형을 관리되는 메모리와 기본 메모리로 이동할 때 사용할 수있는 도우미입니다.TypeBuilder 및 Emit을 통해 구현할 때 인터페이스 서명을 올바르게 가져올 수 없습니다.

나는 일반적으로 생각했다.하지만 정의한 인터페이스를 제대로 지원하려면 생성 유형을 얻을 수 없다. 마지막으로 TypeBuilder.CreateType()을 호출 할 때마다 형식이 인터페이스의 두 메서드 중 첫 번째를 구현하지 못한다는 예외를 throw합니다. 두 번째 메서드도 지원하지 않는다고 생각하지만, 그렇지 않습니다. 그걸 멀리 가져와. DefineGenericParameters를 사용하거나 사용하지 않고 수행했습니다. 대신 실제 유형을 전달합니다. 예를 들어, byte * 매개 변수와 같이 많은 변형을 시도했습니다. 나는 그것을 쓸 수없는 값 유형이라고 말하는 일반 매개 변수를 지정하려고했습니다. 여기 방출과 인터페이스에 대한 다른 질문을 살펴 보았습니다. 그런 질문이 무엇을 의미하는지 생각해 봤습니다.

간단한 조정이 필요한지, 아니면 기본적으로 누락 된 항목이 있는지 알 수 없습니다.

using System; 
using System.Reflection; 
using System.Reflection.Emit; 
using System.Threading; 

namespace Testing 
{ 
    class Program 
    { 
     static unsafe void Main(string[] args) 
     { 
      NativeValueAccessFactory factory = new NativeValueAccessFactory(); 
      INativeValueAccessor<XTestStruct> caller = (INativeValueAccessor<XTestStruct>)factory.GetNativeValueAccessor<XTestStruct>(); 
      byte[] buffer = new byte[1024]; 
      XTestStruct item1 = new XTestStruct(1000, 2000); 

      fixed (byte* pBuffer = &buffer[0]) 
      { 
       caller.WriteData(item1, pBuffer); 
      } 

      XTestStruct item2 = new XTestStruct(1, 1); 
      fixed (byte* pBuffer = &buffer[0]) 
      { 
       item2 = caller.ReadData(pBuffer); 
      } 

      // Compare item1 and item2 
     } 
    } 

    public struct XTestStruct 
    { 
     public int i; 
     public int j; 
     public XTestStruct(int i, int j) { this.i = i; this.j = j; } 
    } 

    public unsafe interface INativeValueAccessor<T> where T : struct 
    { 
     T ReadData(byte* data); 
     void WriteData(T item, byte* data); 
    } 

    public class NativeValueAccessFactory 
    { 
     public NativeValueAccessFactory() { } 

     public object GetNativeValueAccessor<T>() where T : struct 
     { 
      Type itemType = typeof(T); 

      var rand = new Random(); 
      var name = string.Format("{0}_{1}", itemType.Name, rand.Next()); 
      var assemblyName = new AssemblyName(name); 
      var assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); 
      var moduleBuilder = assemblyBuilder.DefineDynamicModule(name + ".dll"); 

      TypeBuilder typeBuilder = moduleBuilder.DefineType(name, TypeAttributes.Public); 
      typeBuilder.AddInterfaceImplementation(typeof(INativeValueAccessor<T>)); 

      // ReadData method 
      MethodBuilder methodBuilder1 = typeBuilder.DefineMethod("ReadData", MethodAttributes.Public | MethodAttributes.Virtual); 

      GenericTypeParameterBuilder[] generics1 = methodBuilder1.DefineGenericParameters("T"); 
      generics1[0].SetGenericParameterAttributes(GenericParameterAttributes.NotNullableValueTypeConstraint); 
      methodBuilder1.SetReturnType(generics1[0]); 
      methodBuilder1.SetParameters(typeof(byte*)); // also tried typeof(byte).MakeByRefType() 

      ILGenerator codeGen1 = methodBuilder1.GetILGenerator(); 
      codeGen1.Emit(OpCodes.Ldarg_1); 
      codeGen1.Emit(OpCodes.Ldobj, itemType); 
      codeGen1.Emit(OpCodes.Ret); 

      // WriteData method 
      MethodBuilder methodBuilder2 = typeBuilder.DefineMethod("WriteData", MethodAttributes.Public | MethodAttributes.Virtual); 

      GenericTypeParameterBuilder[] generics2 = methodBuilder2.DefineGenericParameters("T"); 
      generics2[0].SetGenericParameterAttributes(GenericParameterAttributes.NotNullableValueTypeConstraint); 
      methodBuilder2.SetReturnType(null); 
      methodBuilder2.SetParameters(generics2[0], typeof(byte*)); 

      ILGenerator codeGen2 = methodBuilder2.GetILGenerator(); 
      codeGen2.Emit(OpCodes.Ldarg_2); 
      codeGen2.Emit(OpCodes.Ldarg_1); 
      codeGen2.Emit(OpCodes.Stobj, itemType); 
      codeGen2.Emit(OpCodes.Ret); 

      Type generatedType = typeBuilder.CreateType(); // Throws exception here saying interface not implemented 
      return Activator.CreateInstance(generatedType); // not at all confident this will work, but not even getting here 
     } 
    } 
} 

I 관리 및 기본 메모리 사이의 구조체를 복사 할 수있는 다른 방법을 많이 알고,하지만 그들 중 대부분은 어느 클래스의 사용자에게 대량으로 느린 (예 : BinaryReader를, Marshal.PtrToStructure) 또는 불편 (필요 복사본을 수행하는 대표자를 제공하는 것 등). 동적 인 방법으로이 작업을 수행 할 수 있는지 정말보고 싶었습니다. 내가 잘못하고있는 작업을 파악할 수 있다면 가능한 것처럼 보입니다.

+0

작년에 emit 라이브러리를 만들 때, 고도로 튜닝 된 (예 : IL에서 최적화 될 수있는) C#으로 샘플 메서드를 작성한 다음 ILSpy에서 코드를 본 다음 추가로 튜닝했지만 일리노이의 95 %를 줘. – SledgeHammer

+0

사실 일리노이가 괜찮다고 생각합니다. 적어도, 그것이 틀렸다면 나는 충분히 확인할 수 없다. 그러나 아마 C#에서 도덕적으로 상응하는 것을 쓸 수 있습니다. 그런 다음 ILSpy가 서명에 대한 단서를 제공하는지 확인하십시오. – BruceCo

+0

제네릭 기본 클래스를 만들어 구현하지 않으면 많은 문제를 예방할 수 있습니다. – Nikolaus

답변

1

내 실수로 동적 메서드가 동적 메서드의 인터페이스 일반에서 일반 매개 변수를 설정하는 데있었습니다. 그건 틀렸어요. 이 시점에서 특정 유형의 T, XTestStruct를 처리하기 때문에 메소드의 올바른 서명은 "XTestStruct ReadData (byte *)"및 "void WriteData (XTestStruct, byte *)"입니다. 동적 메서드에서 이러한 매개 변수를 일반적인 것으로 처리함으로써 기본적으로 서명을 "T ReadData (byte *)"로 설정했습니다. 이는 올바르지 않습니다.

TypeBuilder typeBuilder = moduleBuilder.DefineType(name, TypeAttributes.Public); 
    typeBuilder.AddInterfaceImplementation(typeof(INativeValueAccessor<T>)); 

    // ReadData method 
    MethodBuilder methodBuilder1 = typeBuilder.DefineMethod("ReadData", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig); 
    methodBuilder1.SetReturnType(itemType); 
    methodBuilder1.SetParameters(typeof(byte*)); 

    ILGenerator codeGen1 = methodBuilder1.GetILGenerator(); 
    codeGen1.Emit(OpCodes.Ldarg_1); 
    codeGen1.Emit(OpCodes.Ldobj, itemType); 
    codeGen1.Emit(OpCodes.Ret); 

    // WriteData method 
    MethodBuilder methodBuilder2 = typeBuilder.DefineMethod("WriteData", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig); 
    methodBuilder2.SetReturnType(null); 
    methodBuilder2.SetParameters(itemType, typeof(byte*)); 

    ILGenerator codeGen2 = methodBuilder2.GetILGenerator(); 
    codeGen2.Emit(OpCodes.Ldarg_2); 
    codeGen2.Emit(OpCodes.Ldarg_1); 
    codeGen2.Emit(OpCodes.Stobj, itemType); 
    codeGen2.Emit(OpCodes.Ret); 

    Type generatedType = typeBuilder.CreateType(); 
    return Activator.CreateInstance(generatedType); 

이, 위의 전체 예제에 삽입, 오류없이 실행 메모리에 데이터를 복사하고 다시 밖으로 읽어 : 이것은 내가 함께 종료 코드입니다.

+0

질문을 올린 다음 죄송합니다. 죄송합니다. 나는 말 그대로 stackoverflow에 등록하고 질문을 제출하기 전에 이것을 작동 시키려고 2 일을 보냈다. 그 다음, 몇 시간 후 나는 내가 어리 석다는 것을 깨달았다. 나는 이것이 누군가의 시간을 낭비하지 않기를 바란다. – BruceCo

+0

해결책을 찾으면 자신의 질문에 대답하는 것이 좋습니다. 다른 사람도 도울 수 있기를 바랍니다. – Nikolaus

관련 문제