Replace ref parameter without using ref keyword (using IL)

I am looking to be able to replace the reference to a parameter object without using the ref keyword.

The reason I avoid using ref is to keep the collection initializer call looking for the Add(T item) method, and I need the collection class to replace the link with a different version of this interface.

I tried several different ways to do this. At first I tried using the undocumented keywords __makeref , __refvalue and __reftype .

Secondly, I tried to create a DynamicMethod with some IL, which tried to imitate what I observed by looking at a disassembled similar call with the ref parameter.

Here is some code to demonstrate:

 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); } } 

Thanks.

+4
source share
1 answer

I am looking to be able to replace the reference to a parameter object without using the ref keyword.

It just won't happen; when you call a method (not ref ), the CLR creates a copy of the passed reference that your method receives. Although your method may change this link to its content in the heart, it has absolutely no access to the link from which the copy was made (the link that was transmitted by the calling method), no matter what trick you are trying to use undocumented keywords or clever CIL.

Also, why are you trying to replace the parameters passed to the collection initializer, outside of me. It smells like code that does what it shouldn't.

+8
source

All Articles