Untuk apa atribut __DynamicallyInvokable?

181

Melihat melalui System.Linq.EnumerableDotPeek saya perhatikan bahwa beberapa metode dibumbui dengan [__DynamicallyInvokable]atribut.

Apa peran yang dimainkan atribut ini? Apakah ini sesuatu yang ditambahkan oleh DotPeek atau apakah ia memainkan peran lain, mungkin memberi tahu kompilator tentang cara terbaik untuk mengoptimalkan metode?

Jamie Dixon
sumber
2
String.Empty juga punya ini, btw.
Marc Gravell
1
Begitu juga IReadOnlyCollection<T>.
Drew Noakes
1
Dan System.ServiceModel v3's BasicHttpBinding.TextEncoding(yang pada V4 telah pindah ke kelas dasar baru dan menjadi HttpBindingBase.TextEncoding)
Ruben Bartelink
itu juga digunakan untuk nilai integer di System enums seperti DayOfWeek
beauXjames
sekali saya punya kasus ketika metode dengan atribut ini dimasukkan dalam perakitan yang dihasilkan (DateTime.AddYears, .Net 4.5)
gdbdable

Jawaban:

139

Itu tidak berdokumen, tetapi sepertinya salah satu optimasi di .NET 4.5. Tampaknya digunakan untuk mengunggah cache info tipe refleksi, membuat kode refleksi berikutnya pada tipe framework umum berjalan lebih cepat. Ada komentar tentang hal itu di Sumber Referensi untuk System.Reflection.Assembly.cs, properti RuntimeAssembly.Flags:

 // Each blessed API will be annotated with a "__DynamicallyInvokableAttribute".
 // This "__DynamicallyInvokableAttribute" is a type defined in its own assembly.
 // So the ctor is always a MethodDef and the type a TypeDef.
 // We cache this ctor MethodDef token for faster custom attribute lookup.
 // If this attribute type doesn't exist in the assembly, it means the assembly
 // doesn't contain any blessed APIs.
 Type invocableAttribute = GetType("__DynamicallyInvokableAttribute", false);
 if (invocableAttribute != null)
 {
     Contract.Assert(((MetadataToken)invocableAttribute.MetadataToken).IsTypeDef);

     ConstructorInfo ctor = invocableAttribute.GetConstructor(Type.EmptyTypes);
     Contract.Assert(ctor != null);

     int token = ctor.MetadataToken;
     Contract.Assert(((MetadataToken)token).IsMethodDef);

     flags |= (ASSEMBLY_FLAGS)token & ASSEMBLY_FLAGS.ASSEMBLY_FLAGS_TOKEN_MASK;
 }

Tanpa petunjuk lebih lanjut apa arti "API yang diberkati". Meskipun jelas dari konteksnya bahwa ini hanya akan bekerja pada tipe dalam kerangka itu sendiri. Seharusnya ada kode tambahan di suatu tempat yang memeriksa atribut yang diterapkan pada jenis dan metode. Tidak tahu di mana itu berada, tetapi mengingat bahwa itu harus perlu memiliki pandangan dari semua jenis .NET untuk mencoba caching, saya hanya bisa memikirkan Ngen.exe.

Hans Passant
sumber
7
Sepertinya nilai yang disimpan digunakan untuk memeriksa apakah API tersedia di WP8.
usr
1
+1 Lihat komentar saya pada OP's Q - satu kasus di mana CLR tampaknya melakukan tipu daya berdasarkan ini adalah dalam menangani gerakan 'sedikit' metode (misalnya turun satu tingkat ke kelas dasar baru) di bawah penyatuan
Ruben Bartelink
2
Itulah trik [TypeForwardTo], sesuatu yang sangat berbeda.
Hans Passant
@HansPassant Menarik - terdengar seperti saya mungkin salah jadi ... tidak terpikir untuk memeriksa perakitan / tipe yang asli. Intinya adalah bahwa pada 4.5 properti yang dikutip (bukan tipe) telah bergerak relatif ke tempat itu pada 3,5 (secara teknis, System.ServiceModel 3.0). Saya berasumsi bahwa unifikasi a la mscorlibreferensi sedang dimainkan tetapi ada banyak yang berputar-putar tentang masalah spesifik saya untuk dilakukan - akan melaporkan kembali dan / atau menghapus nada menyesatkan ke komentar saya pada waktunya ...
Ruben Bartelink
1
@HansPassant Dari penelitian lebih lanjut ... Tidak dapat melihat apa pun Jenis Penerusan melakukan hal-hal selain Jenis Penerusan sehingga pada titik ini saya mohon berbeda dengan sesuatu yang sedikit berbeda . Kekuatan yang sedang bekerja adalah bahwa ketika Anda memiliki referensi rakitan CLR2 System.ServiceModel v3, memuatnya di bawah pemutakhiran otomatis CLR4 ke System.ServiceModel v4. Bit yang menyenangkan adalah bahwa .NET 4.5 melakukan pembaruan di tempat untuk bit System.ServiceModelmenjatuhkan kelas dasar baru di bawahnya dan memindahkan properti ke tingkat bawah .
Ruben Bartelink
23

Saya menemukan bahwa itu digunakan dalam Runtime*Info.IsNonW8PFrameworkAPI()rangkaian metode internal. Memiliki atribut ini ditempatkan pada anggota membuat IsNonW8PFrameworkAPI () kembali falseuntuk itu dan dengan demikian membuat anggota tersedia di aplikasi WinRT dan menutup The API '...' cannot be used on the current platform.pengecualian.

Penulis profiler harus menempatkan atribut ini pada anggota yang dipancarkan oleh profiler mereka ke dalam kerangka kerja majelis, jika mereka ingin mengaksesnya di bawah WinRT.

Stefan Dragnev
sumber
1
Ya, kode yang ditemukan oleh @Hans mengatur bendera dicari oleh RuntimeAssembly.InvocableAttributeCtorToken, yang dipanggil dengan IsNonW8PFrameworkAPI()metode yang Anda sebutkan.
Mark Hurd