web-dev-qa-db-ja.com

属性クラスはどのように機能しますか?

私の検索では、属性を使用してクラスに適用する方法を説明するガイドのみが表示され続けています。私は自分の属性クラスを作成する方法とそれらがどのように機能するかのメカニズムを学びたいです。

属性クラスはどのようにインスタンス化されますか?それらは、それらが適用されるクラスがインスタンス化されるときにインスタンス化されますか?それが適用されるインスタンス化されたクラスごとに1つがインスタンス化されますか?例えば。 SerializableAttributeクラスをMyDataクラスに適用し、5つのMyDataインスタンスをインスタンス化すると、バックグラウンドで作成されたSerializbleAttributeクラスのインスタンスが5つ存在しますか?または、それらすべての間で共有されるインスタンスは1つだけですか?

属性クラスインスタンスは、関連付けられているクラスにどのようにアクセスしますか? SerializableAttributeクラスは、データをシリアル化できるように、適用可能なクラスにどのようにアクセスしますか?何らかのSerializableAttribute.ThisIsTheInstanceIAmAppliedToプロパティがありますか? :)または、何かをシリアル化するたびにMyClassインスタンスを渡すSerialize関数が反射的に属性を通過してSerialiableAttributeインスタンスを見つけるという逆方向に機能しますか?

56
AaronLS

これまでの日々の仕事で属性を使用したことはありませんが、属性については読んだことがあります。また、ここで言うことをバックアップするために、いくつかのテストを行いました。私がどこかで間違っている場合-これを教えてください:)

私が知っていることから、属性は通常のクラスとして機能していません。それらが適用されるオブジェクトを作成するときにインスタンス化されません。静的インスタンスではなく、オブジェクトのインスタンスごとに1つではありません。また、適用されているクラスにもアクセスしません。

代わりに、それらはクラスのプロパティ(属性?:P)のように機能します。 .NETクラスpropertiesとは異なり、「ガラスの1つのプロパティは透明度」という種類のプロパティに似ています。リフレクションからクラスに適用されている属性を確認し、それに応じて処理することができます。それらは基本的にクラス定義に添付されるメタデータであり、そのタイプのオブジェクトではありません。

クラス、メソッド、プロパティなどの属性のリストを取得しようとすることができます。これらの属性のリストを取得すると、これがインスタンス化されます。次に、これらの属性内のデータを操作できます。

例えば。 Linqテーブルのプロパティには、どのテーブル/列を参照するかを定義する属性があります。ただし、これらのクラスはこれらの属性を使用しません。代わりに、DataContextは、linq式ツリーをSQLコードに変換するときに、これらのオブジェクトの属性をチェックします。

ここで実際の例をいくつか示します。これらは LinqPad で実行したので、奇妙なDump()メソッドについて心配する必要はありません。それをConsole.WriteLineに置き換えて、知らない人がコードを理解しやすくしました:)

_void Main()
{
    Console.WriteLine("before class constructor");
    var test = new TestClass();
    Console.WriteLine("after class constructor");

    var attrs = Attribute.GetCustomAttributes(test.GetType()).Dump();
    foreach(var attr in attrs)
        if (attr is TestClassAttribute)
            Console.WriteLine(attr.ToString());
}

public class TestClassAttribute : Attribute
{
    public TestClassAttribute()
    {
        DefaultDescription = "hello";
        Console.WriteLine("I am here. I'm the attribute constructor!");
    }
    public String CustomDescription {get;set;}
    public String DefaultDescription{get;set;}

    public override String ToString()
    {
        return String.Format("Custom: {0}; Default: {1}", CustomDescription, DefaultDescription);
    }
}

[Serializable]
[TestClass(CustomDescription="custm")]
public class TestClass
{
    public int Foo {get;set;}
}
_

このメソッドのコンソール結果は次のとおりです。

_before class constructor
after class constructor
I am here. I'm the attribute constructor!
Custom: custm; Default: hello
_

そしてAttribute.GetCustomAttributes(test.GetType())はこの配列を返します:(表はすべてのエントリで利用可能なすべての列を示しています。したがって、Serializable属性にはこれらのプロパティはありません:)) LinqPad Attributes Array

他に質問は?遠慮なく尋ねてください!

UPD:私はあなたが質問をするのを見ました:なぜそれらを使うのですか?例として、XML-RPC.NETライブラリについて説明します。 xml-rpcメソッドを表すメソッドを使用して、XML-RPCサービスクラスを作成します。現在の主なものは、XmlRpcではメソッド名にドットなどの特殊文字を含めることができることです。したがって、flexlabs.ProcessTask()xml rpcメソッドを使用できます。

このクラスは次のように定義します。

_[XmlRpcMethod("flexlabs.ProcessTask")]
public int ProcessTask_MyCustomName_BecauseILikeIt();
_

これにより、必要に応じてパブリック名を使用しながら、好きな方法でメソッドに名前を付けることができます。

35
Artiom Chilaru

属性は基本的に、コードのさまざまな部分に添付できるメタデータです。このメタデータは、その後、インタロゲートになり、特定の操作の動作に影響を与える可能性があります。

属性は、コードのほぼすべての側面に適用できます。たとえば、アセンブリに関連付けられているバージョン番号を管理するAssemblyVersionおよびAssemblyFileVersion属性のように、アセンブリレベルで属性を関連付けることができます。

[Assembly: AssemblyVersion("1.0.0.0")]
[Assembly: AssemblyFileVersion("1.0.0.0")]

次に、たとえばSerializable属性を型宣言に適用して、型にシリアル化をサポートするフラグを付けることができます。実際、この属性はCLR内で特別な意味を持ち、ILのタイプに直接特殊なディレクティブとして保存されます。これは、ビットフラグとして保存されるように最適化されており、より効率的に処理できます。この性質は、擬似カスタム属性と呼ばれます。

さらに他の属性をメソッド、プロパティ、フィールド、列挙、戻り値などに適用できます。このリンクを見ると、属性を適用できる可能なターゲットの概要を知ることができます http://msdn.Microsoft .com/en-us/library/system.attributetargets(VS.90).aspx

これに加えて、独自のカスタム属性を定義して、属性が対象とする適切なターゲットに適用することができます。次に、実行時にコードがカスタム属性に含まれる値を反映し、適切なアクションを実行できます。

単純な例として、これは単なる例です:)クラスをデータベース内のテーブルに自動的にマップし、クラスのプロパティをテーブルの列にマップする永続化エンジンを作成することができます。あなたは2つのカスタム属性を定義することから始めることができます

TableMappingAttribute
ColumnMappingAttribute

これをクラスに適用できます。例として、Personクラスがあります。

[TableMapping("People")]
public class Person
{
  [ColumnMapping("fname")]
  public string FirstName {get; set;}

  [ColumnMapping("lname")]
  public string LastName {get; set;}
}

これがコンパイルされるとき、コンパイラーがカスタム属性によって定義された追加のメタデータを出力するという事実以外は、影響はほとんどありません。ただし、Personクラスのインスタンスの属性を動的に検査してデータをPeopleテーブルに挿入し、FirstNameプロパティのデータをfname列に、LastNameプロパティをlname列にマッピングできるPersistanceManagerを作成できるようになりました。

属性のインスタンスに関する質問については、属性のインスタンスはクラスの各インスタンスに対して作成されません。 Peopleのすべてのインスタンスは、TableMappingAttributeおよびColumnMappingAttributesの同じインスタンスを共有します。実際、属性インスタンスは、最初に属性を実際に照会したときにのみ作成されます。

16
Chris Taylor

属性は、クラスまたはメソッド定義(アセンブリメタデータに埋め込まれている)にアタッチされたポストイットと考えてください。

次に、これらのタイプをリフレクトすることで受け入れ、これらのポストイットを探し、それらを異なる方法で処理するプロセッサー/ランナー/インスペクターモジュールを使用できます。これは宣言型プログラミングと呼ばれます。型にコードを記述する代わりに、いくつかの動作を宣言します。

  • 型の直列化可能属性は、直列化するように構築されていることを宣言します。 XmlSerializerは、このクラスのオブジェクトを受け入れ、必要な処理を実行できます。シリアル化/非表示にする必要のあるメソッドを、適切なポストイットでマークします。
  • 別の例はNUnitです。 NUnitランナーは、ターゲットアセンブリで定義されたすべてのクラスの[TestFixture]属性を調べて、テストクラスを識別します。次に、[Test]属性でマークされたメソッドを探してテストを特定し、テストを実行して結果を表示します。

MSDNのこのチュートリアル を実行することをお勧めします。これには、質問のほとんどが最後に例とともに回答されています。コードを複製する代わりに、Audit(Type anyType);というメソッドを抽出することもできます。この例では、属性を検査して「情報を出力」していますが、同じように何でも実行できます。

6
Gishu

はい、彼らはあなたがそれを与えるパラメータでインスタンス化されています。

属性はクラスに「アクセス」しません。属性は、リフレクションデータのクラス/プロパティの属性リストにアタッチされます。

[Serializable]
public class MyFancyClass
{ ... }

// Somewhere Else:

public void function()
{
   Type t = typeof(MyFancyClass);
   var attributes = t.GetCustomAttributes(true);

   if (attributes.Count(p => p is SerializableAttribute) > 0)
   {
       // This class is serializable, let's do something with it!

   }     
}
6
Aren

このダウンロード可能なオープンソースコード LINQ to Active Directory(CodePlex) に注目すると、Bart De Smetがすべての属性クラス定義を書き込んだAttributes.csファイルのメカニズムが興味深いかもしれません。 。私はそこで属性を学びました。

つまり、 Attribute クラスを特殊化し、必要に応じていくつかの特殊なプロパティをコーディングできます。

public class MyOwnAttributeClass : Attribute {
    public MyOwnAttributeClass() {
    }
    public MyOwnAttributeClass(string myName) {
        MyName = myName;
    }
    public string MyName { get; set; }
}

そして、MyOwnAttributeClassが役立つ場所ならどこでも使用できます。これは、クラス定義またはプロパティ定義のいずれかにある可能性があります。

[MyOwnAttributeClass("MyCustomerName")]
public class Customer {
    [MyOwnAttributeClass("MyCustomerNameProperty")]
    public string CustomerName { get; set; }
}

次に、次のようにリフレクションで取得できます。

Attribute[] attributes = typeof(Customer).GetCustomAttribute(typeof(MyOwnAttributeClass));

大括弧の間に置く属性は常に属性のコンストラクターであると考えてください。したがって、パラメーター化された属性が必要な場合は、コンストラクターをそのようにコーディングする必要があります。

このコードは現状のまま提供されており、コンパイルできない場合があります。その目的は、それがどのように機能するかについてのアイデアをあなたに与えることです。

実際、通常は、クラスとプロパティでは異なる属性クラスが必要です。

お役に立てれば!

2

完全な答えを出す時間はあまりありませんが、Reflectionを使用して値に適用されている属性を見つけることができます。それらを作成する場合は、属性クラスから継承してそこから作業します。属性で指定した値は、属性クラスのコンストラクターに渡されます。

あなたが言うことができるかもしれないように、久しぶりです...

マーティン

1
Martin Milan