I am looking to be able to replace the object reference of a parameter without having to use the ref keyword.
The reason that I am avoiding using ref is to preserve collection initializer invocation which looks for an Add(T item)
method, and I need to have the collection class replace the reference with a different implementation of it's interface.
I have tried a couple of different ways to of doing this. First I tried using the undocumented keywords __makeref
, __refvalue
and __reftype
.
Secondly I tried to create a DynamicMethod
with some IL that tried to mimic what I observed from looking at a disassembled similar call with a 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);
}
}
Thank you.
I am looking to be able to replace the object reference of a parameter without having to use the ref keyword.
This is simply not going to happen; when your (non-ref
) method is called, the CLR creates a copy of the passed reference, which your method receives. While your method can modify that reference to its heart's content, it has absolutely no access to the reference from which the copy was made (the reference that was passed in by the calling method) no matter what trickery you attempt using undocumented keywords or clever CIL.
Also, why you're trying to replace parameters passed into a collection initializer is beyond me. It smells of code that does things it shouldn't.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With