Можно ли замаскировать ссылку с помощью интерфейса без выделения кучи?

Имея следующие два класса

interface IMask
{
    string GetName();
}

readonly struct StructSource : IMask
{
    string IMask.GetName()
    {
        throw new NotImplementedException();
    }
}

Как мы можем передать ссылку на ValueType, приведенную в интерфейс? Поэтому мне не нужно распространять общий аргумент в дальнейшем. Можно ли добиться этого без выделения кучи?

Предположим следующий идеальный мир.

using System.Runtime.CompilerServices;

void UseValue<T>(ref T value) where T : IMask
{
    ref IMask ptr = ref Unsafe.As<T, IMask>(ref value);
    UseInterfaceReference(in ptr);
}
void UseInterfaceReference(in IMask target)
{
    target.GetName();
}

но это не работает должным образом, ссылка обнуляется, возможно, из-за несоответствия типов.

Почему бы не пропустить все это с помощью Unsafe? Другой метод требует IMask, у вас есть IMask, так что еще вам нужно? Просто передайте value методу

Thomas Weller 17.07.2024 13:51

«Правильный» подход здесь — это то, что у вас уже есть в UseValue, то есть ограниченный вызов (<T> с where T : IWhatever)

Marc Gravell 17.07.2024 14:22

@ThomasWeller, поэтому правдоподобным решением было бы использовать ((IMask*)&value) ? Если он решает проблему, как предложено (без выделения кучи), это достаточно хорошо, единственная проблема, которую я вижу в этом, - это накладные расходы на добавление повсюду «unsafe» и «{}».

James Jonatah 17.07.2024 15:34

@ThomasWeller, попытка использовать ((IMask*)&value) не сработала. У меня произошло то же самое, доступ к указателю через ->GetName() throws nullref

James Jonatah 17.07.2024 15:42
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
63
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как мы можем передать ссылку на ValueType, приведенную к интерфейс? Поэтому мне не нужно распространять общий аргумент вниз по линия. Можно ли добиться этого без выделения кучи?

Я думаю, что вы имеете в виду:

Могу ли я просто использовать один универсальный метод (UseValue<T>), который ограничен T:IMask, в качестве точки входа, чтобы я мог в дальнейшем выполнять неупаковочные вызовы интерфейса IMask в других неограниченных неуниверсальных методах?

Ответ - нет.

Вы можете понять, почему на самом деле, если бы у вас было две версии UseValue, которые просто печатали результат (без ref для упрощения).

void UseValueNonGeneric(IMask value){
    var toPrint = value.GetName();
    Console.WriteLine(toPrint);
}

void UseValue<T>(T value)
where T : IMask {
    var toPrint = value.GetName();
    Console.WriteLine(toPrint);
}

По сути, они делают одно и то же, но отличаются в одном — как компилятор генерирует код для фактического вызова интерфейса.

В нетиповом случае имеем

callvirt instance string IMask::GetName()

в общем случае имеем:

constrained. !!T
callvirt instance string IMask::GetName()

Во втором случае вы хотите избежать упаковки (распределения), как указано в документации об ограниченном префиксе:

Когда инструкция метода callvirt имеет префикс constrained thisType, инструкция выполняется следующим образом:

  • ...
  • Если thisType является типом значения, а thisType реализует метод, тогда ptr передается в неизмененном виде как указатель this на метод вызова.
  • ...

Таким образом, независимо от того, удалось ли вам выполнить приведение или нет, вы потерпите неудачу, потому что для того, чтобы нераспределение (неупаковка) вообще могло произойти, вам нужен ограниченный общий метод.

И это просто не сработает:

void UseInterfaceReference(in IMask target)
{
    target.GetName();
}

Другие вопросы по теме