Nameof с Generics

Я экспериментировал с nameof с дженериками. Я не получил результат, которого ожидал. Я не уверен, что это часть спецификации или нет.

class MainClass
{
    public static void Main (string[] args)
    {
        Console.WriteLine ($"Hello { nameof(FooBar<string>)! }");
    }
}

class FooBar<T> { }

Выход, который я получаю,

Привет, FooBar!

Я бы ожидал некоторых подробностей о параметрах типа.

Я попробовал его с помощью метода и не выполнил ошибку компилятора:

class MainClass
{
    public static void Main (string[] args)
    {
        Console.WriteLine ($"Hello { nameof(Do<string>) }");
    }

    public static T Do<T>() {}
}

Ошибка CS8084: аргумент для оператора nameof не может быть группой методов с аргументами типа (CS8084) (foo)

Это потому, что nameof является конструкцией времени компиляции, а generics - это типы, инициализированные во время выполнения? Или есть другие ограничения?

Ответы

Ответ 1

Я бы ожидал некоторых подробностей о параметрах типа

"spec" говорит:

Результат nameof. Результат nameof зависит от символов, которые его аргумент, связанный с:

Один или несколько членов: если все члены имеют одинаковое имя метаданных, результатом nameof является это имя; в противном случае это ошибка "This аргумент ссылается на несколько элементов с разными именами". имя метаданных члена я or я < isA1... AK > `просто "я" после стандартного преобразования идентификаторов.

Параметр <T> удаляется из-за стандартных преобразований идентификатора (раздел § 2.2.4 в спецификации С#), который не позволяет <> как действительные идентификаторы. Сначала удаляется любое из ведущих @, затем удаляются последовательности Unicode, а затем удаляются любые символы форматирования. Это, конечно же, происходит во время компиляции. Вы также можете увидеть это при попытке распечатать имя родового типа:

typeof(List<string>).Name;

приведет к:

List`1

Это потому, что nameof является конструкцией времени компиляции и генериками типы инициализируются во время выполнения? Или есть другие ограничения?

Вторая ошибка указана как недопустимая по дизайну, чтобы избежать осложнений разрешения перегрузки внутри nameof:

Разрешить общие аргументы типа? Предположительно "да" при именовании типа так как это выражение уже работает. И предположительно "нет". при присвоении имени группе методов, поскольку аргументы типа используются/выводятся во время разрешения перегрузки, и было бы справиться с этим в nameof.

Мы ясно видим это в кодовой базе roslyn:

private BoundExpression BindNameofOperatorInternal(InvocationExpressionSyntax node, 
                                                   DiagnosticBag diagnostics)
{
    CheckFeatureAvailability(node.GetLocation(), MessageID.IDS_FeatureNameof, diagnostics);

    var argument = node.ArgumentList.Arguments[0].Expression;
    string name = "";

    // We relax the instance-vs-static requirement for top-level member access expressions by creating a NameofBinder binder.
    var nameofBinder = new NameofBinder(argument, this);
    var boundArgument = nameofBinder.BindExpression(argument, diagnostics);

    if (!boundArgument.HasAnyErrors && CheckSyntaxForNameofArgument(argument, out name, diagnostics) && boundArgument.Kind == BoundKind.MethodGroup)
    {
        var methodGroup = (BoundMethodGroup)boundArgument;
        if (!methodGroup.TypeArgumentsOpt.IsDefaultOrEmpty)
        {
            // method group with type parameters not allowed
            diagnostics.Add(ErrorCode.ERR_NameofMethodGroupWithTypeParameters, argument.Location);
        }
        else
        {
            nameofBinder.EnsureNameofExpressionSymbols(methodGroup, diagnostics);
        }
    }

    return new BoundNameOfOperator(node, boundArgument, ConstantValue.Create(name), Compilation.GetSpecialType(SpecialType.System_String));
}