开发者

How to "box" a value WITHOUT using programming language's boxing support?

I want to box a value without using whatever .NET language's built-in support for that.

That is, given an enum value I want an reference type object that represents that value and its type.

This is a subgoal of being able to pass enum values from late binding pure C++ code, a possible solution of that, so, I'm not looking for how to use e.g. C# boxing (that's easy, and irrelevant in so many ways).

The following code yields ...

c:\projects\test\csharp\hello\main.cs(6,26): error CS0122: 'System.Ref开发者_开发知识库lection.RuntimeFieldInfo' is inaccessible due to its protection level

However, using the more documented FieldInfo class, which is what the signature of MakeTypedReference requires, I get an exception saying that the argument isn't RuntimeFieldInfo.

The unsuccessful code, experimental, 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 );
        }
    }
}

An answer that helps making the above code "work" would be very nice.

An answer that points out another way to achieve boxing (perhaps the above is completely and utterly wrong? - it's just experimental) would also be very nice! :-)

EDIT: Clarification: essentially I'm after the same as C# (object)v yields. I have tried the enum ToObject method, but unfortunately while that presumably works OK within .NET, on the C++ side I just get back the 32-bit integer value. The problem on the C++ side is that passing an integer as third arg of e.g. MessageBox.Show just fails, presumably because the default binder on the .NET side doesn't convert it to enum type, so I suspect a reference object of suitable type is needed for actual argument.


I'm not sure exactly what sort of boxing you want, but if you want a TypedReference, just use __makeref() in C#. Here's a working version of your program:

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);
        }
    }
}


You may be looking for the hidden keyword '__makeref' rather than TypedReference.MakeTypedReference

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

//  s = "OK"


Any routine that can take something of type Object could be used to store value types, without employing the Framework's boxing support, by creating a single-element array for each value-type instance to be stored, and storing a reference to that. Compilers could support things like Debug.Print which need to accept a variable number of arbitrary-type parameters by having a special declaration which would request that the compiler pass a System.Array[], each element of which would be a single-element array that wrapped a single parameter without regard for whether it was a class type or value type. If such support existed, it would be possible for the Debug.Print routine to know the storage-location type of each parameter as well as the type of the object instance therein (e.g. in code like

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

the Debug.Print routine would receive a System.Array[3] holding references to a Cat[1], an Animal[1], and an IPettable[1].

Note that without boxing support, structures that implement interfaces could not be inherently cast directly to interface references; this would sometimes be a limitation, but imposing that limitation could make it possible for value types to define conversions to and from interface types. If desired, a compiler could auto-define a class type for each struct type that implements an interface and define conversions between the struct type and that interface type. Such a thing effectively happens with the type system except that the reference type associated with each value type is generated at run-time rather than by the compiler.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜