Почему я не могу использовать интерполяцию строк в атрибуте?

Я пишу модульные тесты (MSTest) в С# 6.0, и я заметил что-то странное в том, как компилятор обрабатывает интерполяцию строк в атрибутах.

Почему это работает:

[TestCategory(nameof(MyClass) + "-UnitTest")]

Когда этого не происходит?

[TestCategory($"{nameof(MyClass)}-UnitTest")]

Игнорируя тот факт, что это может быть отличный способ классифицировать мои тесты; Мне любопытно, почему компилятор разрешает один, а не другой.

Ответ 1

Когда компилятор встречает интерполированную строку, он немедленно преобразует ее в вызов String.Format поэтому...

[TestCategory($"{nameof(MyClass)}-UnitTest")]

Становится...

[TestCategory(string.Format("{0}-UnitTest", nameof(MyClass)))]

Атрибуты требуют, чтобы их аргументы были постоянными выражениями, но указанное выше выражение не будет оцениваться до момента выполнения, следовательно, ошибка...

CS0182 Аргумент атрибута должен быть константным выражением, выражением typeof или выражением создания массива типа параметра атрибута...

Вы знаете, что вся информация доступна во время компиляции, но компилятор недостаточно умен, чтобы понять это.

nameof работает немного иначе, чем интерполированные строки, потому что он вычисляется во время компиляции, поэтому нет ошибки.

Ответ 2

Интерполированные строки не являются постоянными значениями. Значение определяется во время выполнения, хотя в вашем случае все входы могут быть вычислены во время компиляции.

Ответ 3

Аргумент атрибута должен быть константой времени компиляции. Хотя nameof() является константой (см. Is nameof(), оцененный во время компиляции?), Функция интерполяции строк сама по себе не является.

Интерполированное строковое выражение создает строку, заменяя содержащиеся выражения указателями ToString результатов выражений.

Ответ 4

Строковая интерполяция происходит во время выполнения, и атрибуты присутствуют во время компиляции. Поэтому ваш компилятор не может решить это, так как он будет скомпилирован так:

[TestCategory(new FormattableString
  {
    Format = "{0}-UnitTest",
    Args = new object[] { nameof(MyClass)}
  })]