web-dev-qa-db-ja.com

ジェネリックスを含むnameof

ジェネリックで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がコンパイル時の構成要素であり、ジェネリックスが実行時に初期化される型であるためですか?それとも他の制限がありますか?

28
Daniel A. White

型パラメーターについての詳細を期待します

「スペック」は言う

nameofの結果。nameofの結果は、その引数がバインドされたシンボルによって異なります。

1つ以上のメンバー:すべてのメンバーが同じメタデータ名を持つ場合、nameofの結果はその名前になります。そうでない場合、「この引数は異なる名前の複数の要素を参照しています」というエラーになります。メンバーのメタデータ名I orI <isA1 ... AK> `は、標準の識別子変換が適用された後は単に「I」になります。

<T>パラメータは、<>を有効な識別子として許可しない標準の識別子変換(C#仕様のセクション§2.4.2)により削除されました。最初に先頭の@が削除され、次にUnicodeエスケープシーケンスが変換されてから、フォーマット文字が削除されます。もちろん、これはコンパイル時にも発生します。ジェネリック型の名前を出力しようとすると、これも確認できます。

typeof(List<string>).Name;

結果は:

List`1

これは、nameofがコンパイル時の構成体であり、ジェネリックスが実行時に初期化される型であるためですか?それとも他の制限がありますか?

2番目のエラーは、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));
}
23
Yuval Itzchakov