2011-07-26 5 views
4

ref 키워드를 사용하지 않고도 매개 변수의 객체 참조를 교체 할 수 있기를 원합니다.ref 키워드 (IL 사용)를 사용하지 않고 매개 변수의 참조 교체

ref를 사용하는 것을 피하는 이유는 Add(T item) 메서드를 찾는 컬렉션 초기화 프로그램 호출을 보존하는 것이고 컬렉션 클래스가 참조를 다른 인터페이스 구현으로 바꿔야 할 필요가 있기 때문입니다.

나는 이것을하기 위해 몇 가지 다른 방법을 시도했다. 먼저 문서화되지 않은 키워드 __makeref, __refvalue__reftype을 사용해 보았습니다.

두 번째로 ref 매개 변수를 사용하여 디스 어셈블 된 유사한 호출을 보면서 관측 한 일부 IL과 함께 DynamicMethod을 만들려고했습니다.

using System; 
using System.Collections.Generic; 
using System.Collections; 
using System.Reflection.Emit; 
using System.Reflection; 
interface IRecord 
{ 
    string Name { get;} 
} 
class ImpA : IRecord 
{ 
    public string Name { get { return "Implementation A"; } } 
} 
class ImpB : IRecord 
{ 
    public string Name { get { return "Implementation B"; } } 
} 
class RecordList<T> : IEnumerable<T> 
{ 
    //// Standard Add method (of course does not work) 
    //public void Add(T item) 
    //{ 
    // item = (T)(object)new ImpB(); 
    //} 

    // ref method (works great but the signature cannot be 
    // used by the collection initializer) 
    public void Add(ref T item) 
    { 
     IRecord newItem = new ImpB(); 
     item = (T)newItem; 
    } 

    //// Using System.TypedReference (does not work) 
    //public void Add(T item) 
    //{ 
    // T newItem = (T)(object)new ImpB(); 
    // TypedReference typedRef = __makeref(item); 
    // __refvalue(typedRef, T) = newItem; 
    //} 

    // Using Reflection.Emit DynamicMethod (This method should work but I need help) 
    public void Add(T item) 
    { 
     IRecord newItem = new ImpB(); 

     System.Reflection.MethodAttributes methodAttributes = 
       System.Reflection.MethodAttributes.Public 
      | System.Reflection.MethodAttributes.Static; 

     DynamicMethod dm = new DynamicMethod("AssignRef", 
      methodAttributes, 
      CallingConventions.Standard, 
      null, 
      new Type[] { typeof(IRecord), typeof(IRecord) }, 
      this.GetType(), 
      true); 

     ILGenerator generator = dm.GetILGenerator(); 
     // IL of method 
     //public static void Add(ref item, ref newItem) 
     //{ 
     // item = newItem; 
     //} 
     // -- Loading Params (before call to Add() -- 
     //L_002b: ldloca.s sb // this is the ref variable 
     //L_002d: ldloc.2 // The other variable 
     // -- Add method IL -- 
     //L_0000: nop 
     //L_0001: ldarg.0 
     //L_0002: ldarg.1 
     //L_0003: stind.ref 
     //L_0004: ret 

     generator.Emit(OpCodes.Ldarga_S, 0); 
     generator.Emit(OpCodes.Ldarg_1); 
     generator.Emit(OpCodes.Stind_Ref); 
     generator.Emit(OpCodes.Ret); 

     Action<IRecord, IRecord> AssignRef = 
      (Action<IRecord, IRecord>)dm.CreateDelegate(
      typeof(Action<IRecord, IRecord>)); 

     AssignRef((IRecord)item, (IRecord)newItem); 
    } 


    public IEnumerator GetEnumerator() 
    { 
     throw new NotImplementedException(); 
    } 
    IEnumerator<T> IEnumerable<T>.GetEnumerator() 
    { 
     throw new NotImplementedException(); 
    } 
} 
class Program 
{ 
    static void Main(string[] args) 
    { 
     IRecord imp = new ImpA(); 
     Console.WriteLine("Original implementation: {0}\n", imp.Name); 

     // Calls Add Implicitly 
     RecordList<IRecord> records = new RecordList<IRecord> { imp }; 
     // Prints "Implementation A" 
     Console.WriteLine("After Add Method: {0}", imp.Name); 

     records.Add(ref imp); // Explicit call with ref 
     // Prints "Implementation B" 
     Console.WriteLine("After Add Ref method: {0}\n", imp.Name); 
    } 
} 

감사합니다 : 여기

이 보여 몇 가지 코드입니다.

+5

정말 추악한 것을 만들려고합니다. 너 정말 필요해/원하니? –

+0

내가 게시 한 코드 스 니펫은 내 질문을 설명하는 데 도움이되는 개념을 보여주기위한 것입니다. 나는 그것이 추악한 무언가를 창조하는 데 사용될 수 있다는 것을 알고 있습니다 - 감사합니다. – RepDbg

답변

8

ref 키워드를 사용하지 않고도 매개 변수의 개체 참조를 대체 할 수 있기를 기대합니다.

이것은 단순히 발생하지 않습니다. 귀하의 (ref) 메서드가 호출되면 CLR은 전달 된 참조의 복사본을 만들어 메서드에서 수신합니다. 귀하의 방법은 그 내용을 마음의 내용으로 수정할 수는 있지만 은 문서화되지 않은 키워드를 사용하여 어떤 속임수를 사용했는지에 상관없이 사본이 만들어진 참조 (호출하는 방법으로 전달 된 참조)에 전혀 액세스 할 수 없습니다 (). 영리한 CIL.

또한 왜 컬렉션 초기화 프로그램에 전달 된 매개 변수를 바꾸는 것이 저를 넘어서는 것입니까? 코드해서는 안되는 일을하는 냄새가납니다.

+0

답장을 보내 주셔서 감사합니다. 저는 이것이 managed C++에서 성취 될 것이라고 생각했습니다. 그렇다면 어떻게 CLR의 한계가 될 수 있습니까? 인터페이스를 사용하는 경우 구현을 전환해야하는 이유는 무엇입니까? – RepDbg

+2

함수에서 참조가 아닌 매개 변수를 수정할 수있는 언어를 모르겠습니다. 이것은 .NET에서 (CLR에서 지원하지 않기 때문에) 수행 할 수 없습니다. 즉 Managed C++ 또는 C++/CLI에서 수행 할 수 없습니다. –