Замените ссылку на параметр, не используя ключевое слово ref (используя IL)
Я ищу, чтобы иметь возможность заменить ссылку на объект параметра без использования ключевого слова ref.
Причина, по которой я избегаю использования ref, заключается в сохранении вызова инициализатора коллекции, который ищет Add(T item)
метод, и мне нужно, чтобы класс коллекции заменил ссылку другой реализацией его интерфейса.
Я попробовал несколько разных способов сделать это. Сначала я попытался использовать недокументированные ключевые слова __makeref
, __refvalue
а также __reftype
,
Во-вторых, я пытался создать DynamicMethod
с некоторыми IL, которые пытались имитировать то, что я наблюдал, глядя на разобранный аналогичный вызов с параметром ref.
Вот некоторый код для демонстрации:
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);
}
}
Спасибо.
1 ответ
Я ищу, чтобы иметь возможность заменить ссылку на объект параметра без использования ключевого слова ref.
Это просто не произойдет; когда ваш (не ref
) вызывается метод, CLR создает копию переданной ссылки, которую получает ваш метод. Несмотря на то, что ваш метод может изменить эту ссылку на содержимое своего сердца, он не имеет абсолютно никакого доступа к ссылке, из которой была сделана копия (ссылка, переданная вызывающим методом), независимо от того, какую хитрость вы пытаетесь использовать, используя недокументированные ключевые слова или умный CIL.,
Кроме того, почему вы пытаетесь заменить параметры, переданные в инициализатор коллекции, я не знаю. Он пахнет кодом, который делает то, что не должен.