はじめに
今回はSourceGenerator
にてAttributeArgumentSyntax.NameColon
がnullな場合と値が格納されている場合の違いについて書きたいと思います。
その前に
まず前提としてSyntaxTree
は構文解析によって得られた情報であり、解析対象の文字列に含まれていないものは取得できません。今回もそれが原因になってます。
またNameColon
は何かというと、AttributeArgumentSyntax
の場合はAttribute
のコンストラクタの引数名を表しています。
// 構文解析の対象 // 今回の場合、NameColonは「flag1」や「flag2」といったコンストラクタの引数名を示す [SampleMarker(flag1: true, flag2: false)] public class SampleClass { }
// 補足 : 定義側 (構文解析の対象外) using System; public class SampleMarkerAttribute : Attribute { public bool Flag1 { get; } public bool Flag2 { get; } public SampleMarkerAttribute(bool flag1, bool flag2) { Flag1 = flag1; Flag2 = flag2; }
取得するには
Symbol
を利用して解析する人が多いと思うので、あまりSyntax
から解析するケースは少ないかもしれませんが、引数名・値を取得しようとしたら以下のようなコードになります
// ---------------------------- // ---- Syntaxを利用した場合 ---- // ---------------------------- // classSyntaxに付与されているAttributeについて、引数名と値を取得する (なんともめんどくさい...) // NOTE: NameColonは名前を明示的に指定しないと、nullが格納されているので注意 var attributeParams = classSyntax.AttributeLists .SelectMany(x => x.Attributes) .Where(x => x.Name.ToFullString() == "SampleMarker") .SelectMany(x => x.ArgumentList.Arguments) .Select(x => new { Name = x.NameColon.Expression.ToFullString(), Value = x.Expression.ToFullString() });
違い
SharpLabを利用すると簡単にわかります。
引数名を明示的に指定すればNameColon
があり、指定しないとnull
が格納されます。
// NameColonあり [SampleMarker(flag1: true, flag2: false)]
// NameColonなし [SampleMarker(true, false)]
原理を考えれば当たり前なことが分かるのですが、そもそもコードに記述されていない情報は構文解析では取得できません。つまり明示的に引数名を指定しないと、構文解析では引数の順番しか分からないのです。
補足
ちなみにコンストラクタではなくsetter
で設定した場合はNameEquals
が設定されます。