C#でカスタム属性を作成する方法


119

私は何度も試しましたが、それでもカスタム属性の使い方を理解できません(すでにたくさんのリンクを調べました)。

コード付きのカスタム属性の非常に基本的な例を誰かに説明してもらえますか?

回答:


96

カスタム属性を作成するコードはかなり単純ですが、属性が何であるかを理解することは非常に重要です。

属性は、プログラムにコンパイルされたメタデータです。属性自体は、クラス、プロパティ、またはモジュールに機能を追加するのではなく、データを追加するだけです。ただし、リフレクションを使用すると、機能を作成するためにこれらの属性を活用できます。

それでは、たとえば、MicrosoftのEnterprise LibraryValidation Application Blockを見てみましょう。コード例を見ると、次のことがわかります。

    /// <summary>
    /// blah blah code.
    /// </summary>
    [DataMember]
    [StringLengthValidator(8, RangeBoundaryType.Inclusive, 8, RangeBoundaryType.Inclusive, MessageTemplate = "\"{1}\" must always have \"{4}\" characters.")]
    public string Code { get; set; }

上記のスニペットから、Validatorのルールに応じて、コードが変更されるたびに常に検証されると推測できます(例では、8文字以上8文字以下)。しかし、真実は属性は何もしないということです。前述のように、プロパティにメタデータを追加するだけです。

ただし、エンタープライズライブラリにはValidation.Validateオブジェクトを調べるメソッドがあり、プロパティごとに、コンテンツが属性によって通知されたルールに違反しているかどうかをチェックします。

したがって、属性について考える必要があります-後で他のメソッド/クラスなどで使用される可能性があるコードにデータを追加する方法です。


私は答えが特に好きです、そして特に、上記のコードのsetステートメントに同じ条件を置くことができるので、属性とはどのように異なるのでしょうか
スラッシュshogdhe

1
@スラッシュ:言い換えることができますか?私はその質問をよく理解していませんでした。
ブルーノブラント

1
スラッシュは、属性の使用と実際の検証コードをプロパティセッター内に配置することの違いについて尋ねることを意図したものだと思います。回答:セッター内にコードを記述して値を検証することはできますが、属性のみを使用しても検証自体は実行されません。属性は単なる「メタデータ」です。別の場所にある別のコードは、使用する属性に関心があり、それらを読み取り、それらに基づいてアクションを実行する必要があります。@BrunoBrantが言及したように、典型的な例は検証ライブラリです。
romar 2013年

10
なぜこれが受け入れられた答えであるのかわからない。実際の質問(Googleでもインデックス化されています)は、「C#でカスタム属性を作成する方法」です。答えは、そのトピックをまったく掘り下げていません。一方、2番目の答えはそうです。
Drakestar 2017

2番目の答えは、質問に関連していると思います。
Mohammad Taherian

267

まず、Attributeから派生するクラスを記述します

public class MyCustomAttribute: Attribute
{
    public string SomeProperty { get; set; }
}

次に、この属性で何でも(クラス、メソッド、プロパティなど)装飾できます。

[MyCustomAttribute(SomeProperty = "foo bar")]
public class Foo
{

}

最後に、リフレクションを使用してそれをフェッチします。

var customAttributes = (MyCustomAttribute[])typeof(Foo).GetCustomAttributes(typeof(MyCustomAttribute), true);
if (customAttributes.Length > 0)
{
    var myAttribute = customAttributes[0];
    string value = myAttribute.SomeProperty;
    // TODO: Do something with the value
}

AttributeUsage属性を使用して、このカスタム属性を適用できるターゲットタイプを制限できます。

/// <summary>
/// This attribute can only be applied to classes
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class MyCustomAttribute : Attribute

属性について知っておくべき重要なこと:

  • 属性はメタデータです。
  • それらは、コンパイル時にアセンブリに組み込まれ、プロパティの設定方法に非常に深刻な影響を与えます。定数(コンパイル時に既知)値のみが受け入れられます
  • カスタム属性を理解して使用する唯一の方法は、Reflectionを使用することです。したがって、実行時にリフレクションを使用してそれらをフェッチし、カスタム属性で何かを装飾しない場合は、それほど多くのことは期待できません。
  • 属性の作成時間は非決定的です。それらはCLRによってインスタンス化され、ユーザーはそれを完全に制御できません。

3
どこで、どの関数/クラスで、「リフレクションを使用してフェッチするか」
Hasan A Yousef

@Hasan A Yousef、たとえばEntity Frameworkには、フレームワークに言う「Key」属性があります。このプロパティは主キーと見なされます。ORMを作成する場合、属性は非常に役立ちます
Parsa

クラスではなくプロパティのカスタム属性にどのようにアクセスしますか?
キャンバス

docs.microsoft.com/en-us/dotnet/standard/attributes/...だけ完全を期すため、このMSDNページは非常によく、それをまとめたもの
バリスAkkurt

ジェネリック医薬品で、それはタイプを取得するには、はるかに簡単です:var value = typeof(Foo).GetCustomAttributes<MyCustomAttribute>().First().SomeProperty;
jpaugh

27

Darin Dimitrovの素晴らしいレスポンスを利用/コピーします。これは、クラスではなくプロパティのカスタム属性にアクセスする方法です。

[クラスのFoo] 装飾されたプロパティ:

[MyCustomAttribute(SomeProperty = "This is a custom property")]
public string MyProperty { get; set; }

それを取得する:

PropertyInfo propertyInfo = typeof(Foo).GetProperty(propertyToCheck);
object[] attribute = propertyInfo.GetCustomAttributes(typeof(MyCustomAttribute), true);
if (attribute.Length > 0)
{
    MyCustomAttribute myAttribute = (MyCustomAttribute)attribute[0];
    string propertyValue = myAttribute.SomeProperty;
}

これをループでスローし、リフレクションを使用して、classのプロパティのこのカスタム属性にアクセスすることもできますFoo

foreach (PropertyInfo propertyInfo in Foo.GetType().GetProperties())
{
    string propertyName = propertyInfo.Name;

    object[] attribute = propertyInfo.GetCustomAttributes(typeof(MyCustomAttribute), true);
    // Just in case you have a property without this annotation
    if (attribute.Length > 0)
    {
        MyCustomAttribute myAttribute = (MyCustomAttribute)attribute[0];
        string propertyValue = myAttribute.SomeProperty;
        // TODO: whatever you need with this propertyValue
    }
}

ダーリン、ありがとう!


プロパティに存在する属性のタイプがわからない場合、これをどのように拡張しますか?object[] attribute = propertyInfo.GetCustomAttributes(typeof(???), true);私はそれらすべてを反復処理して、m1()不明な各属性のメソッドを呼び出したいだけです
heyNow

0

短い答えは、c#で属性を作成するためのもので、Attributeクラスから継承する必要があるだけです。これは:)

しかし、ここでは属性について詳しく説明します。

基本的に属性は、アセンブリ、クラス、メソッド、プロパティ、フィールドなどにロジックを適用するために使用できるクラスです。

.Netでは、Microsoftは([必須]、[StringLength(100)]、[Range(0、999.99)])などの廃止または検証属性などのいくつかの定義済み属性を提供しています。また、asp.netにActionFiltersのような属性がありますこれは、必要なロジックをコードに適用するのに非常に役立ちます(学習に情熱を傾けている場合は、アクションフィルターに関するこの記事を参照してください)。

もう1つのポイントは、AttibuteUsageを介して属性に一種の構成を適用できることです。

  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)]

AttributeUsageで属性クラスを装飾するとき、この属性をどこで使用するかをc#コンパイラーに伝えることができます。これは、クラス、プロパティのアセンブリ、または...で使用し、属性の使用を許可されます定義されたターゲット(クラス、アセンブリ、プロパティなど)で数回ですか?

属性に関するこの定義の後で、例を示します。大学で新しいレッスンを定義し、大学の管理者とマスターだけが新しいレッスンを定義できるようにしたいとします。

namespace ConsoleApp1
{
    /// <summary>
    /// All Roles in our scenario
    /// </summary>
    public enum UniversityRoles
    {
        Admin,
        Master,
        Employee,
        Student
    }

    /// <summary>
    /// This attribute will check the Max Length of Properties/fields
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)]
    public class ValidRoleForAccess : Attribute
    {
        public ValidRoleForAccess(UniversityRoles role)
        {
            Role = role;
        }
        public UniversityRoles Role { get; private set; }

    }


    /// <summary>
    /// we suppose that just admins and masters can define new Lesson
    /// </summary>
    [ValidRoleForAccess(UniversityRoles.Admin)]
    [ValidRoleForAccess(UniversityRoles.Master)]
    public class Lesson
    {
        public Lesson(int id, string name, DateTime startTime, User owner)
        {
            var lessType = typeof(Lesson);
            var validRolesForAccesses = lessType.GetCustomAttributes<ValidRoleForAccess>();

            if (validRolesForAccesses.All(x => x.Role.ToString() != owner.GetType().Name))
            {
                throw new Exception("You are not Allowed to define a new lesson");
            }
            
            Id = id;
            Name = name;
            StartTime = startTime;
            Owner = owner;
        }
        public int Id { get; private set; }
        public string Name { get; private set; }
        public DateTime StartTime { get; private set; }

        /// <summary>
        /// Owner is some one who define the lesson in university website
        /// </summary>
        public User Owner { get; private set; }

    }

    public abstract class User
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime DateOfBirth { get; set; }
    }


    public class Master : User
    {
        public DateTime HireDate { get; set; }
        public Decimal Salary { get; set; }
        public string Department { get; set; }
    }

    public class Student : User
    {
        public float GPA { get; set; }
    }



    class Program
    {
        static void Main(string[] args)
        {

            #region  exampl1

            var master = new Master()
            {
                Name = "Hamid Hasani",
                Id = 1,
                DateOfBirth = new DateTime(1994, 8, 15),
                Department = "Computer Engineering",
                HireDate = new DateTime(2018, 1, 1),
                Salary = 10000
            };
            var math = new Lesson(1, "Math", DateTime.Today, master);

            #endregion

            #region exampl2
            var student = new Student()
            {
                Name = "Hamid Hasani",
                Id = 1,
                DateOfBirth = new DateTime(1994, 8, 15),
                GPA = 16
            };
            var literature = new Lesson(2, "literature", DateTime.Now.AddDays(7), student);
            #endregion

            ReadLine();
        }
    }


}

プログラミングの現実の世界では、属性を使用するためにこのアプローチを使用しない可能性があります。属性を使用することの教育的理由のため、私はこれを述べました

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.