web-dev-qa-db-ja.com

属性と名前付き/オプションのコンストラクターパラメーターが機能しない

私は次のように定義されたカスタム属性を持っています:

  [AttributeUsage(AttributeTargets.Field)]
  public class EnumDisplayAttribute : Attribute
  {
    public string Description { get; private set; }
    public string Code { get; private set; }

    public EnumDisplayAttribute(string description = null, string code = null)
    {
      Description = description;
      Code = code;
    }
  }

両方のコンストラクターパラメーターはオプションです。

このようなフィールドでこの属性を使用する場合

  public enum TransactionType
  {
    [EnumDisplay(code: "B")] 
    Bill,
    [EnumDisplay(description: null, code: "C")]
    CashReceipt,
  }

コードエディタに波線は表示されませんが、ファイル行番号の列がない漠然としたエラーが表示されます。エラーメッセージは次のとおりです。

エラーCS0182:属性引数は、定数式、typeof式、または属性パラメーターtypeの配列作成式である必要があります

エラーをクリックしても何も起こりません。つまり、エラーサイトに移動することはありません(明らかに、行番号と列がないため)。

次のように属性を設定した場合でも、次のようになります。

[EnumDisplay("This is a Bill")] 

コンパイラはそれを好きではありません。

事実上、この属性を属性として使用するために、両方のパラメーター(名前付きかどうか)を指定する必要があります。

もちろん、この属性を次のように通常のクラスとして使用する場合:

var enumDisplayAttribute = new EnumDisplayAttribute();
enumDisplayAttribute = new EnumDisplayAttribute(description: "This is a Bill");
enumDisplayAttribute = new EnumDisplayAttribute(code: "B");
enumDisplayAttribute = new EnumDisplayAttribute(description: "This is a Bill", code: "B");
enumDisplayAttribute = new EnumDisplayAttribute("This is a Bill", "B");
enumDisplayAttribute = new EnumDisplayAttribute("This is a Bill");

コンパイラは、上記の「スタイル」のいずれかを受け入れます。

確かに、何かが足りないか、脳が機能していないだけです。

22
Shiv Kumar

属性のオプション値がC#にすでに存在していた後、オプションのパラメーターがC#に追加されました。したがって、オプションの属性パラメーターについては、属性固有の構文にフォールバックする必要があります。

_[AttributeUsage(AttributeTargets.Field)]
public class EnumDisplayAttribute : Attribute
{
    public string Description { get; set; }
    public string Code { get; set; }

    public EnumDisplayAttribute()
    {
    }
}

public enum TransactionType
{
    [EnumDisplay(Code = "B")] 
    Bill,
    [EnumDisplay(Description = null, Code = "C")]
    CashReceipt,
}
_

ご覧のとおり、最終結果は事実上同じですが、名前付き引数を使用する代わりに、名前付きプロパティを使用しています([EnumDisplay(Description = null, Code = "C")]のような構文は属性宣言でのみ可能です)。

別の見方をすれば、属性宣言はメソッド/コンストラクターの呼び出しから構文を「借用」しますが、属性宣言はそれ自体メソッドの呼び出しではないため、メソッドと同じ機能をすべて取得するわけではありません。 。

28
Kirk Woll

コンストラクターを使用して値を属性にプッシュしたい場合(たとえば、属性のプロパティの一部が必須である場合、またはそれらに対して何らかの処理を実行する場合)、いつでも古い学校に行ってコンストラクターをオーバーロードできます。

例えば:

[AttributeUsage(AttributeTargets.Field)]
public class SampleAttribute : Attribute
{
    public string MandatoryProperty { get; private set; }
    public string OptionalProperty { get; private set; }

    // we use an overload here instead of optional parameters because 
    // C# does not currently support optional constructor parameters in attributes
    public SampleAttribute(string mandatoryProperty)
        : this(mandatoryProperty, null)
    {
    }

    public SampleAttribute(string mandatoryProperty, string optionalProperty)
    {
        MandatoryProperty = mandatoryProperty;
        OptionalProperty = optionalProperty;
    }
}
5

オプションのパラメーターは実際にはオプションではなく、メソッドのシグネチャにはすべての引数が含まれ、属性は特別です(オプションのパラメーターの前に存在し、属性として適用されると異なるルールがあります(たとえば、誰が属性コンストラクターを呼び出すかを検討してください))。ただし、将来的にはサポートが追加されると思います。

今のところ、オプションの効果を実現したい場合は、次のことを試してください。

[AttributeUsage(AttributeTargets.Field)]
public class EnumDisplayAttribute : Attribute
{
  public string Description { get; set; }
  public string Code { get; set; }

}

そして、そのように適用します:

[EnumDisplay(Description = null, Code = "C")]
private object _aField;
3
Rich O'Kelly