2013-01-16 52 views
8
  • 有沒有一種方法來標記類型(甚至更好,一個接口),使得沒有它的實例可以被存儲在一個字段(以類似的方式來TypedReferenceArgIterator)?
  • 以同樣的方式,是否有辦法阻止實例通過匿名方法傳遞,以及 - 通常 - 模仿上述兩種類型的行爲?
  • 這可以通過ILDasm或更一般地通過IL編輯來完成嗎?由於UnconstrainedMelody通過對已編譯程序集的二進制編輯實現了通常無法獲得的結果,也許有一種方法可以通過相同的方法「標記」某些類型(甚至更好,抽象的或標記接口)。

我懷疑它是在編譯器中的硬編碼,因爲documentation for the error CS0610狀態:制式的情況下,不可儲存

有一些類型,而且不能被用作字段或屬性。這些類型包括...

在我看來,這意味着像這些類型的集合可以擴展 - 但我可能是錯的。

我在SO上搜索了一下,雖然我知道throwing a compiler error以編程方式無法完成,但是我找不到任何消息來源指出某些「特殊」類型的行爲無法複製。

即使這個問題主要是學術問題,也可能有一些用法來解答。例如,有時確定某個對象的生存期限制於創建它的方法塊可能會很有用。

編輯:RuntimeArgumentHandle是一個更多(未提及)不可儲存類型。

編輯2:如果它有什麼用處,似乎CLR以不同的方式對待這些類型爲好,如果不是唯一的編譯器(仍假設類型不以任何方式與別人不同)。例如,下面的程序將對TypedReference*發出TypeLoadException。我已經調整它使其縮短,但你可以解決所有你想要的。將指針的類型改爲void*不會拋出異常。

using System; 

unsafe static class Program 
{ 
    static TypedReference* _tr; 

    static void Main(string[] args) 
    { 
     _tr = (TypedReference*) IntPtr.Zero; 
    } 
} 
+5

如果它*是*硬編碼,順便說一句,我不會感到驚訝。僅僅因爲文檔不想把它放下來並不意味着它不是在編譯器中完成的。 –

+0

我想同樣的事情,但我必須檢查。 –

+1

[CS0611](http://msdn.microsoft.com/en-US/library/bfca7x6z(v = vs80).aspx)是一個相關的錯誤,儘管可能解決方案或者適用於兩者。 – Mir

回答

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參考版本的共享源代碼。我不會通過我所做的所有追蹤,以避免在超出所有合理使用範圍的情況下膨脹此答案,但最終,您最終會在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 \虛擬機\類。 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

很好的答案,我認爲這是解決它。 – Mir

2

我在IL中找不到任何表明這些類型有特殊含義的東西。我知道編譯器有許多很多特殊情況,例如將int/Int32轉換爲內部類型int32或許多與Nullable結構有關的東西。我高度懷疑這些類型也是特例。

一個可能的解決方案將是Roslyn,我期望會讓你創建這樣一個約束。

+0

我還沒有看到很多Roslyn,但我認爲使用常規編譯器的最終用戶不會受到約束的影響。我會等着看是否有人能夠提出反駁或確認(通過檢查編譯器,不知何故)。 – Mir

+0

@Eve我會假設,如果你正在做一件這樣罕見的事情,那你就可以毫無問題地使用Roslyn。當然,如果它是一種公共類型,你可能無法做到這一點。 –

+0

你認爲Roslyn會如何幫助? – svick

相關問題