__makeref как способ получить справочное значение в C #? - PullRequest
2 голосов
/ 04 апреля 2019

Я видел этот код, который использовался с указанием ссылочного значения:

static void Main(string[] args)
{
    string s1 = "ab";
    string s2 = "a"+"b";
    string s3 = new StringBuilder("a").Append("b").ToString();

    Console.WriteLine(GetMemoryAddress(s1));
    Console.WriteLine(GetMemoryAddress(s2));
    Console.WriteLine(GetMemoryAddress(s3));
}

static IntPtr GetMemoryAddress(object s1)
{
    unsafe
    {
        TypedReference tr = __makeref(s1);
        IntPtr ptr = **(IntPtr**) (&tr);
        return ptr;
    }
}

Результат (как и ожидалось):

enter image description here

(я знаю, что здесь начинается интернирование строк, но это не вопрос).

Вопрос:

Хотя кажется, что это делает работу, Использует ли __makeref это правильный способ получения эталонного значения в c #?

Или есть ситуации, в которых это ^ не получится ....?

1 Ответ

3 голосов
/ 04 апреля 2019

Хотя кажется, что он делает свою работу, использование __makeref является ли правильным способом получения эталонного значения в c #?

Не существует «правильного» способа сделать это в C # - это не то, что вы должны попробовать и сделать, но: с точки зрения того, что делает - это, по сути, полагается на внутреннем макете TypedReference и приведении типа; это будет работать (до тех пор, пока TypedReference не изменится внутренне - например, изменяется порядок полей Type и Value), но ... это неприятно.

Существует более прямой подход; в IL вы можете молча преобразовать из управляемого указателя в неуправляемый указатель . Это означает, что вы можете сделать что-то неприятное, например:

unsafe delegate void* RefToPointer(object obj);
static RefToPointer GetRef { get; } = MakeGetRef();
static RefToPointer MakeGetRef()
{
    var dm = new DynamicMethod("evil", typeof(void*), new[] { typeof(object) });
    var il = dm.GetILGenerator();
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Ret);
    return (RefToPointer)dm.CreateDelegate(typeof(RefToPointer));
}

и теперь вы можете просто сделать:

var ptr = new IntPtr(GetRef(o));
Console.WriteLine(ptr);

Это ужасно , и вы никогда не должны этого делать - и, конечно, GC может перемещать вещи, когда вы не смотрите (или даже когда вы смотрите), но ... это работает.

Вопрос о том, является ли ref-emit «лучше», чем недокументированные и неподдерживаемые языковые функции, такие как __makeref и приведение типов: является вопросом некоторых дискуссий. Надеюсь чисто академические дебаты!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...