Как "упаковать" значение БЕЗ использования поддержки бокса языка программирования?

Я хочу пометить значение, не используя встроенную поддержку языка.NET для этого.

То есть, учитывая значение enum, я хочу объект ссылочного типа, который представляет это значение и его тип.

Это подзадача, состоящая в возможности передавать значения enum из позднего связывания чистого кода C++, возможное решение этого, поэтому я не ищу, как использовать, например, бокс C# (это легко и неактуально во многих отношениях).

Следующий код дает...

c: \ projects \ test \ csharp \ hello \ main.cs (6,26): ошибка CS0122: "System.Reflection.RuntimeFieldInfo" недоступен из-за уровня защиты

Тем не менее, используя более документированные FieldInfo класс, который является то, что подпись MakeTypedReference требует, я получаю исключение, говорящее, что аргумент не RuntimeFieldInfo,

Неудачный код, экспериментальный, C#:

using System.Windows.Forms;
using Type = System.Type;
using TypedReference = System.TypedReference;
using MethodInfo = System.Reflection.MethodInfo;
using FieldInfo = System.Reflection.FieldInfo;
using RuntimeFieldInfo = System.Reflection.RuntimeFieldInfo;

namespace hello
{
    class Startup
    {
        static void Main( string[] args )
        {
            Type        stringType      = typeof( string );
            Type        messageBoxType  = typeof( MessageBox );
            Type        mbButtonsType   = typeof( MessageBoxButtons );
            Type        mbIconType      = typeof( MessageBoxIcon );
            Type[]      argTypes        = { stringType, stringType, mbButtonsType };// }, mbIconType };
            MethodInfo  showMethod      = messageBoxType.GetMethod( "Show", argTypes );

//          object      mbOkBtn         = (object) (MessageBoxButtons) (0);
            TypedReference tr           = TypedReference.MakeTypedReference(
                mbButtonsType,
                new RuntimeFieldInfo[]{ mbIconType.GetField( "OK" ) }
                );
            object      mbOkBtn         = TypedReference.ToObject( tr );

            object[]    mbArgs          = { "Hello, world!", "Reflect-app:", mbOkBtn };

            showMethod.Invoke( null, mbArgs );
        }
    }
}

Ответ, который помогает сделать приведенный выше код "работающим", был бы очень хорош.

Ответ, который указывает на другой способ достижения бокса (возможно, вышеизложенное совершенно и совершенно неверно? - это просто эксперимент), также был бы очень хорош!:-)

РЕДАКТИРОВАТЬ: Уточнение: по сути, я после того же, как C# (object)v доходность. Я пробовал перечисление ToObject метод, но, к сожалению, хотя это, по-видимому, работает нормально в.NET, на стороне C++ я просто возвращаю 32-битное целочисленное значение. Проблема на стороне C++ состоит в том, что передача целого числа в качестве третьего аргумента, например, MessageBox.Show просто терпит неудачу, предположительно потому, что компоновщик по умолчанию на стороне.NET не преобразует его в тип enum, поэтому я подозреваю, что для фактического аргумента необходим ссылочный объект подходящего типа.

3 ответа

Я не уверен, какой именно бокс вы хотите, но если вы хотите TypedReferenceПросто используйте __makeref() в C#. Вот рабочая версия вашей программы:

using System.Windows.Forms;
using System;
using MethodInfo = System.Reflection.MethodInfo;

namespace hello
{
    class Startup
    {
        static void Main(string[] args)
        {
            Type stringType = typeof(string);
            Type messageBoxType = typeof(MessageBox);
            Type mbButtonsType = typeof(MessageBoxButtons);
            Type[] argTypes = { stringType, stringType, mbButtonsType };
            MethodInfo showMethod = messageBoxType.GetMethod("Show", argTypes);

            var OkBtn = MessageBoxButtons.OK;
            TypedReference tr = __makeref(OkBtn);
            object mbOkBtn = TypedReference.ToObject(tr);

            object[] mbArgs = { "Hello, world!", "Reflect-app:", mbOkBtn };

            showMethod.Invoke(null, mbArgs);
        }
    }
}

Возможно, вы ищете скрытое ключевое слово "__makeref", а не TypedReference.MakeTypedReference

var v = MessageBoxButtons.OK;
var tr = __makeref(v);
var obj = TypedReference.ToObject(tr);
var s = obj.ToString();

//  s = "OK"

Любая процедура, которая может принять что-то типа Object может использоваться для хранения типов значений без использования поддержки бокса в Framework путем создания одноэлементного массива для каждого сохраняемого экземпляра типа значения и сохранения ссылки на него. Компиляторы могут поддерживать такие вещи, как Debug.Print которые должны принимать переменное число параметров произвольного типа, имея специальное объявление, которое потребовало бы, чтобы компилятор передал System.Array[]каждый элемент которого был бы массивом с одним элементом, который обернул единственный параметр независимо от того, был ли это тип класса или тип значения. Если бы такая поддержка существовала, это было бы возможно для Debug.Print подпрограмма для определения типа места хранения каждого параметра, а также типа экземпляра объекта в нем (например, в коде, подобном

Cat Meowser = new SiameseCat();
Animal Ralph = new PersianCat();
IPettable Mindy = new MixedBreedCat();
Debug.Print("{0:T} {1:T} {2:T}", Meowser, Ralph, Mindy);

Debug.Print рутина получит System.Array[3] держа ссылки на Cat[1], Animal[1]и IPettable[1],

Обратите внимание, что без поддержки бокса структуры, которые реализуют интерфейсы, не могли бы быть по своей природе приведены непосредственно к ссылкам интерфейса; иногда это может быть ограничением, но наложение этого ограничения может позволить типам значений определять преобразования в и из типов интерфейса. При желании компилятор может автоматически определять тип класса для каждого типа структуры, который реализует интерфейс, и определять преобразования между типом структуры и этим типом интерфейса. Такая вещь эффективно происходит с системой типов, за исключением того, что ссылочный тип, связанный с каждым типом значения, генерируется во время выполнения, а не компилятором.

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