好的。这不是一个完整的分析,但我怀疑它足以确定你是否可以做到这一点,即使通过玩弄 IL - 据我所知,你不能。
我还在用 dotPeek 查看反编译版本时,看不到那里的特定类型/那些特定类型的任何特别之处,无论是属性还是其他:
namespace System
{
/// <summary>
/// Describes objects that contain both a managed pointer to a location and a runtime representation of the type that may be stored at that location.
/// </summary>
/// <filterpriority>2</filterpriority>
[ComVisible(true)]
[CLSCompliant(false)]
public struct TypedReference
{
所以,完成后,我尝试使用 System.Reflection.Emit 创建这样一个类:
namespace NonStorableTest
{
//public class Invalid
//{
// public TypedReference i;
//}
class Program
{
static void Main(string[] args)
{
AssemblyBuilder asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("EmitNonStorable"),
AssemblyBuilderAccess.RunAndSave);
ModuleBuilder moduleBuilder = asmBuilder.DefineDynamicModule("EmitNonStorable", "EmitNonStorable.dll");
TypeBuilder invalidBuilder = moduleBuilder.DefineType("EmitNonStorable.Invalid",
TypeAttributes.Class | TypeAttributes.Public);
ConstructorBuilder constructorBuilder = invalidBuilder.DefineDefaultConstructor(MethodAttributes.Public);
FieldBuilder fieldI = invalidBuilder.DefineField("i", typeof (TypedReference), FieldAttributes.Public);
invalidBuilder.CreateType();
asmBuilder.Save("EmitNonStorable.dll");
Console.ReadLine();
}
}
}
当你运行它时,它会抛出一个 TypeLoadException,它的堆栈跟踪将你指向 System.Reflection.Emit.TypeBuilder.TermCreateClass。所以然后我用反编译器去追求它,它给了我这个:
[SuppressUnmanagedCodeSecurity]
[SecurityCritical]
[DllImport("QCall", CharSet = CharSet.Unicode)]
private static void TermCreateClass(RuntimeModule module, int tk, ObjectHandleOnStack type);
指向 CLR 的非托管部分。在这一点上,为了不被打败,我挖掘了 CLR 参考版本的共享源。我不会进行我所做的所有跟踪,以避免使这个答案超出所有合理使用范围,但最终你会在 MethodTableBuilder::SetupMethodTable2 函数中进入 \clr\src\vm\class.cpp (这似乎也设置了字段描述符),您可以在其中找到这些行:
// Mark the special types that have embeded stack poitners in them
if (strcmp(name, "ArgIterator") == 0 || strcmp(name, "RuntimeArgumentHandle") == 0)
pClass->SetContainsStackPtr();
和
if (pMT->GetInternalCorElementType() == ELEMENT_TYPE_TYPEDBYREF)
pClass->SetContainsStackPtr();
后者与 \src\inc\cortypeinfo.h 中的信息有关,因此:
// This describes information about the COM+ primitive types
// TYPEINFO(enumName, className, size, gcType, isArray,isPrim, isFloat,isModifier)
[...]
TYPEINFO(ELEMENT_TYPE_TYPEDBYREF, "System", "TypedReference",2*sizeof(void*), TYPE_GC_BYREF, false, false, false, false)
(它实际上是该列表中唯一的 ELEMENT_TYPE_TYPEDBYREF 类型。)
然后,ContainsStackPtr() 又在不同地方的其他地方使用,以防止使用这些特定类型,包括在字段中 - 来自 \src\vm\class.cp,MethodTableBuilder::InitializeFieldDescs():
// If it is an illegal type, say so
if (pByValueClass->ContainsStackPtr())
{
BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, IDS_CLASSLOAD_BAD_FIELD, mdTokenNil);
}
无论如何:长话短说,似乎以这种方式不可存储的类型被有效地硬编码到 CLR 中,因此,如果您想更改列表或提供一种 IL 方法将类型标记为不可存储,您几乎将不得不采用 Mono 或共享源 CLR 并衍生出您自己的版本。