什么是(fnptr)*类型以及如何创建它?

以下IL代码创建名为(fnptr)*的Type实例(标记0x2000000 – 无效,模块mscorlib.dll)。

 ldtoken method void* ()* call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) 

这种类型的目的是什么? 是否可以在C#中创建此类型实例而无需编写任何IL代码,也许可以使用reflection? 令牌上的Module.ResolveType抛出ArgumentOutOfRangeException

编辑:

很明显, (fnptr)类型是IL方法指针类型的内部CLR类型表示,但是当删除最后一个* ,它只返回IntPtr

编辑#2:

(fnptr)来自一个可以在SSCLI typestring.cpp中看到的函数

 // ...or function pointer else if (ty.IsFnPtrType()) { // Don't attempt to format this currently, it may trigger GC due to fixups. tnb.AddName(L"(fnptr)"); } 

为什么基本的fnptr返回IntPtr可以在typehandle.cpp中看到:

 OBJECTREF TypeHandle::GetManagedClassObject() const { 

[…]

  switch(GetInternalCorElementType()) { case ELEMENT_TYPE_ARRAY: case ELEMENT_TYPE_SZARRAY: case ELEMENT_TYPE_BYREF: case ELEMENT_TYPE_PTR: return ((ParamTypeDesc*)AsTypeDesc())->GetManagedClassObject(); case ELEMENT_TYPE_VAR: case ELEMENT_TYPE_MVAR: return ((TypeVarTypeDesc*)AsTypeDesc())->GetManagedClassObject(); // for this release a function pointer is mapped into an IntPtr. This result in a loss of information. Fix next release case ELEMENT_TYPE_FNPTR: return TheIntPtrClass()->GetManagedClassObject(); default: _ASSERTE(!"Bad Element Type"); return NULL; } } } 

所以看起来他们忘了解决它。

我不知道你在问什么,为什么你认为有什么不对。 (fnptr)*是指向非托管函数指针的指针的类型名称。 它在CLR中获得特殊处理确实很奇怪,我怀疑这是一个考古工艺品。 可以追溯到.NET之前和代表们发明之前的时间。 CLR起源于Project 42中的“通用运行时”,这是.NET之前的失败项目。

也许一些C ++ / CLI代码演示如何生成一个很有用,向您展示如何创建它:

 #include "stdafx.h" using namespace System; typedef void (*functionPointer)(int); ref class Example { public: functionPointer* fp; }; int main(array ^args) { auto field = Example::typeid->GetField("fp"); auto name = field->FieldType->FullName; Console::WriteLine(name); return 0; } 

输出: (fnptr)*

不知道你在哪里看到FNPTR被宣布。

对于此代码:

 .assembly extern mscorlib {} .assembly Test { .ver 1:0:1:0 } .module test.exe .method static void main() cil managed { .maxstack 1 .entrypoint ldtoken method void* ()* call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) ldtoken method void* () call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) ret } 

ILASM(4.5.22.0)输出以下内容:

 .method privatescope static void main$PST06000001() cil managed { .entrypoint // Code size 21 (0x15) .maxstack 1 IL_0000: ldtoken method void *()* IL_0005: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) IL_000a: ldtoken method void *() IL_000f: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) IL_0014: ret } // end of method 'Global Functions'::main 

更新#1:

也许我在这里很密集,但我没有看到从这段代码生成FNPTR:

 typeof(StringBuilder).ToString(); 

IL看起来像这样:

 IL_0000: nop IL_0001: ldtoken [mscorlib]System.Text.StringBuilder IL_0006: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) IL_000b: callvirt instance string [mscorlib]System.Object::ToString() IL_0010: pop IL_0011: ret 

Type.ToString()调用是一个callvirt操作,因为ToString()是一个虚方法 。

虚函数通常表现为函数指针的结构,我想这会导致发出FNPTR。 如果省略* in ()*得到() ,则现在描述的是函数,而不是函数指针。

当您看到FNPTR时,您使用的是什么版本的.NET? 你用什么来提取IL?

可以将指针的签名加载函数指针:

 public static unsafe Type GetTypeFromFieldSignature(byte[] signature, Type declaringType = null) { declaringType = declaringType ?? typeof(object); Type sigtype = typeof(Type).Module.GetType("System.Signature"); Type rtype = typeof(Type).Module.GetType("System.RuntimeType"); var ctor = sigtype.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new[]{typeof(void*), typeof(int), rtype}, null); fixed(byte* ptr = signature) { object sigobj = ctor.Invoke(new object[]{(IntPtr)ptr, signature.Length, declaringType}); return (Type)sigtype.InvokeMember("FieldType", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetProperty, null, sigobj, null); } } var fnptrPtr = GetTypeFromFieldSignature(new byte[]{6, 15, 27, 0, 0, 1}); 

6是字段,15是指针,27是函数指针,0,0,1是没有返回或参数的方法签名。