Для чего нужен атрибут __DynamicallyInvokable?

181

Просматривая System.Linq.Enumerableв DotPeek, я замечаю, что некоторые методы имеют [__DynamicallyInvokable]атрибут.

Какую роль играет этот атрибут? Это что-то добавленное DotPeek или оно играет другую роль, возможно, информируя компилятор о том, как лучше оптимизировать методы?

Джейми Диксон
источник
2
String.Empty также имеет это, кстати.
Марк Гравелл
1
Так и делает IReadOnlyCollection<T>.
Дрю Ноакс
1
И System.ServiceModel v3«S BasicHttpBinding.TextEncoding(который в V4 перешел на новый базовый класс и становится HttpBindingBase.TextEncoding)
Рубен Bartelink
он также используется для целочисленных значений в системных перечислениях, таких как DayOfWeek
beauXjames
как только у меня есть случай, когда метод с этим атрибутом был встроен в сгенерированную сборку (DateTime.AddYears, .Net 4.5)
gdbdable

Ответы:

139

Он недокументирован, но выглядит как одна из оптимизаций в .NET 4.5. По-видимому, он используется для заполнения информационного кэша типа отражения, что ускоряет выполнение последующего кода отражения на общих типах платформы. Об этом есть комментарий в справочном источнике для System.Reflection.Assembly.cs, свойство 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;
 }

Без дальнейших намеков, что может означать «благословенный API». Хотя из контекста ясно, что это будет работать только с типами в самой структуре. Где-то должен быть дополнительный код, который проверяет атрибут, примененный к типам и методам. Не знаю, где это находится, но, учитывая, что для кэширования необходимо иметь представление обо всех типах .NET, я могу думать только о Ngen.exe.

Ганс Пассант
источник
7
Похоже, что сохраненное значение используется для проверки доступности API в WP8.
USR
1
+1 Смотрите мой комментарий к Q ОП - один случай, когда CLR, кажется, делает хитрость, основываясь на этом, заключается в обработке «небольших» перемещений методов (например, вниз на один уровень в новый базовый класс) при объединении
Рубен Бартелинк,
2
Это трюк [TypeForwardTo], нечто совершенно другое.
Ганс Пассант
@HansPassant Интересно - звучит так, будто я могу ошибаться, так что ... не думал об исследовании оригинальной сборки / типа. Суть в том, что на 4.5 цитируемое свойство (не тип) переместилось относительно того, где оно было на 3.5 (технически, System.ServiceModel 3.0). Я предполагал, что объединение а-ля mscorlibссылки было в игре, но у меня есть много вопросов по моей конкретной проблеме, чтобы все равно поступить
сообщу
1
@ HansPassant Из дальнейших исследований ... Не могу увидеть, что переадресация типов делает что-то, кроме пересылки типов, так что на этом этапе я позволю себе отличаться чем- то совершенно другим . Сила в работе заключается в том, что, когда у вас есть ссылка на сборку CLR2 System.ServiceModel v3, она загружается в CLR4 с автоматическим обновлением до System.ServiceModel v4. Самое интересное в том, что .NET 4.5 выполняет обновление на месте для System.ServiceModelдобавления нового базового класса внизу и перемещает свойство на уровень ниже .
Рубен Бартелинк
23

Я обнаружил, что он используется в Runtime*Info.IsNonW8PFrameworkAPI()наборе внутренних методов. Размещение этого атрибута на элементе возвращает IsNonW8PFrameworkAPI () falseдля него и, таким образом, делает элемент доступным в приложениях WinRT и закрываетThe API '...' cannot be used on the current platform. исключение.

Авторы профилировщика должны поместить этот атрибут в элементы, испускаемые их профилировщиком в сборки фреймворка, если они хотят получить к ним доступ в WinRT.

Стефан Драгнев
источник
1
Да, код, найденный @Hans, устанавливает искомые флаги RuntimeAssembly.InvocableAttributeCtorToken, которые вызываются упомянутыми IsNonW8PFrameworkAPI()вами методами.
Марк Херд