2013-01-16 21 views
8
  • Czy istnieje sposób, aby zaznaczyć typ (lub nawet lepiej, interfejs), tak że nie ma przypadków może być przechowywany w polu (w sposób podobny do TypedReference i ArgIterator)?
  • W ten sam sposób, istnieje sposób, aby uniemożliwić przekazywanie instancji metodami anonimowymi i - Ogólnie - Aby naśladować zachowanie dwóch powyższych typów?
  • Czy można to zrobić poprzez ILDasm lub bardziej ogólnie poprzez edycję IL? Ponieważ UnconstrainedMelody osiąga normalnie nieosiągalne wyniki przez binarną edycję skompilowanego zespołu, być może istnieje sposób na "zaznaczenie" pewnych typów (lub nawet lepszych, abstrakcyjnych lub interfejsów znacznikowych) za pomocą tego samego podejścia.

wątpię, to ustalony w kompilator ponieważ documentation for the error CS0610 stanach:Marka Typ jest non-Chowany

Istnieje kilka rodzajów, które nie mogą być stosowane jako pola lub właściwości. Te typy obejmują ...

Co według mnie sugeruje, że zestaw takich typów może zostać przedłużony - Ale mogę się mylić.

Szukałem trochę SO i gdy rozumiem, że programowo nie można wykonać throwing a compiler error, nie mogłem znaleźć żadnego źródła stwierdzającego, że pewne zachowania "specjalnych" typów nie mogły być replikowane.

Nawet jeśli pytanie jest w większości akademickie, może istnieć kilka sposobów na odpowiedź. Na przykład czasami przydatne może być upewnienie się, że czas życia danego obiektu jest ograniczony do bloku metody, który go tworzy.

EDYTOWANIE:RuntimeArgumentHandle to kolejny (nie wymieniony) typ niezapisujący.

EDIT 2: Jeśli może być dowolnego użytku, wydaje się, że CLR traktuje te typy w inny sposób, jak również, jeśli nie tylko kompilator (wciąż zakładając, że typy są w żaden sposób nie różni się od innych). Poniższy program, na przykład, rzuci TypeLoadException dotyczące TypedReference*. Zaadaptowałem go, aby był krótszy, ale możesz go obejść, jak tylko chcesz. Zmiana typu wskaźnika na, powiedzmy, void* nie spowoduje odrzucenia wyjątku.

using System; 

unsafe static class Program 
{ 
    static TypedReference* _tr; 

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

Nie byłbym wcale zaskoczony, gdyby * był * zakodowany na stałe. Tylko dlatego, że dokumentacja nie chce tego sparować, nie oznacza, że ​​nie jest to robione w kompilatorze. –

+0

Myślę, że to samo, ale muszę sprawdzić. –

+1

[CS0611] (http://msdn.microsoft.com/en-US/library/bfca7x6z (v = vs.80) .aspx) jest pokrewnym błędem, chociaż prawdopodobnie rozwiązanie zastosowane do obu. – Mir

Odpowiedz

5

OK. To nie jest kompletna analiza, ale podejrzewam, że jest ona wystarczająca do ustalenia, czy możesz to zrobić, nawet przez twiddling IL - co, o ile mogę powiedzieć, nie możesz.

ja też, patrząc na wersji decompiled z dotPeek, nie widział niczego specjalnego na temat tego konkretnego typu/typów w tych szczególnych tam, atrybut mądry albo inaczej:

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 
    { 

Tak, że gotowe starałem stworzenie takiej klasy przy użyciu 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(); 
     } 
    } 
} 

, że przy uruchomieniu go, rzuca TypeLoadException, którego ślad stosu punktów do System.Reflection.Emit.TypeBuilder.TermCreateClass. Więc poszedłem dalej z dekompilatorem, który dał mi to:

[SuppressUnmanagedCodeSecurity] 
[SecurityCritical] 
[DllImport("QCall", CharSet = CharSet.Unicode)] 
private static void TermCreateClass(RuntimeModule module, int tk, ObjectHandleOnStack type); 

Wskazanie w niezarządzanych częściach CLR. W tym momencie, aby nie zostać pokonanym, zagłębiłem się we wspólne źródła dla referencyjnej wersji CLR. Nie przejdę przez to wszystko, co zrobiłem, aby uniknąć nadania tej odpowiedzi poza wszelkie uzasadnione użycie, ale ostatecznie kończy się to w \ clr \ src \ vm \ class.cpp, w funkcji MethodTableBuilder :: SetupMethodTable2 (który pojawia się również założyć deskryptory pola), gdzie można znaleźć te linie:

// Mark the special types that have embeded stack poitners in them 
         if (strcmp(name, "ArgIterator") == 0 || strcmp(name, "RuntimeArgumentHandle") == 0) 
          pClass->SetContainsStackPtr(); 

i

if (pMT->GetInternalCorElementType() == ELEMENT_TYPE_TYPEDBYREF) 
          pClass->SetContainsStackPtr(); 

Ten ostatni odnoszące się do informacji zawartych w katalogu \ src \ inc \ cortypeinfo.h, a więc:

// 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) 

(Jest to w rzeczywistości jedyny typ ELEMENT_TYPE_TYPEDBYREF na tej liście).

Funkcja ContainsStackPtr() jest następnie używana w innych miejscach w różnych miejscach, aby zapobiec używaniu tych konkretnych typów, w tym w polach - od \ 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); 
} 

Zresztą: wyciąć długo, długo, krótko mówiąc, wydaje się być tak, że jakie typy są dla magazynować w ten sposób jest skuteczny sprzęt komputerowy zakodowane w CLR, a więc jeśli chcesz zmienić listę lub podać IL oznacza oznaczenie typów jako niemagazyrowanych, będziesz prawie musiał wziąć Mono lub CLR z podziałem źródła i wydzielić swoje własny versio n.

+1

Świetna odpowiedź, myślę, że to załatwia. – Mir

2

Nie mogłem znaleźć w IL nic, co wskazywałoby, że te typy są w jakikolwiek sposób wyjątkowe. Wiem, że kompilator ma wiele, wiele specjalnych przypadków, takich jak przekształcanie int/Int32 w wewnętrzny typ int32 lub wiele rzeczy dotyczących struktury Nullable. Podejrzewam, że te typy to również przypadki szczególne.

Możliwe rozwiązanie to Roslyn, co, jak sądzę, pozwoliłoby na stworzenie takiego ograniczenia.

+0

Nie zajrzałem jeszcze do Roslyn, ale zakładam, że ograniczenie to nie wpłynęłoby na użytkownika końcowego, używającego zwykłego kompilatora. Zaczekam, czy ktoś może wymyślić kontrargument lub potwierdzenie (przez zbadanie kompilatora, jakoś). – Mir

+0

@ Zakładam, że jeśli robisz coś tak rzadkiego jak to, będziesz mógł bez problemu używać Roslyn. Oczywiście, jeśli jest to typ publiczny, możesz nie być w stanie tego zrobić. –

+0

Jak myślisz, w jaki sposób Roslyn może pomóc? – svick