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.
精彩评论