TypedReference
和ArgIterator
类似)? 我怀疑它在编译器中是硬编码的,因为documentation for the error CS0610状态:
有些类型不能用作字段或属性。这些类型包括......
我认为哪些类型可以扩展 - 但我可能错了。
我在SO上搜索了一下,虽然我知道throwing a compiler error无法以编程方式完成,但我找不到任何来源说某些“特殊”类型的行为无法复制。
即使问题主要是学术问题,也可能会有一些答案。例如,有时可以确保某个对象的生命周期被约束到创建它的方法块。
编辑: RuntimeArgumentHandle
是另一种(未提及的)不可存储类型。
编辑2:如果它有任何用处,似乎CLR也以不同的方式处理这些类型,如果不仅仅是编译器(仍假设类型不在与其他人不同的方式)。例如,以下程序将针对TypeLoadException
抛出TypedReference*
。我已经对它进行了调整以缩短它,但你可以随心所欲地解决它。将指针的类型更改为,例如void*
不会抛出异常。
using System;
unsafe static class Program
{
static TypedReference* _tr;
static void Main(string[] args)
{
_tr = (TypedReference*) IntPtr.Zero;
}
}
答案 0 :(得分:5)
好。这不是一个完整的分析,但我怀疑这是一个足以确定你是否可以做到这一点的目的,即使是错误的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参考版本的共享源代码。我不会通过我所做的所有跟踪,以避免使这个答案超出所有合理用途,但最终,你最终在/ clr \ src \ _vm \ class.cpp中,在MethodTableBuilder :: SetupMethodTable2函数中(这也似乎设置了字段描述符),你可以在这里找到这些行:
// 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并剥离您自己的版本。
答案 1 :(得分:2)
我在IL中找不到任何表明这些类型有任何特殊之处的东西。我知道编译器有许多特殊情况,例如将int
/ Int32
转换为内部类型int32
,或许多与Nullable
结构相关的内容。我非常怀疑这些类型也是特殊情况。
一个可能的解决方案是Roslyn,我希望能让你创建这样的约束。