サーチ…
カスタム属性の作成
//1) All attributes should be inherited from System.Attribute
//2) You can customize your attribute usage (e.g. place restrictions) by using System.AttributeUsage Attribute
//3) You can use this attribute only via reflection in the way it is supposed to be used
//4) MethodMetadataAttribute is just a name. You can use it without "Attribute" postfix - e.g. [MethodMetadata("This text could be retrieved via reflection")].
//5) You can overload an attribute constructors
[System.AttributeUsage(System.AttributeTargets.Method | System.AttributeTargets.Class)]
public class MethodMetadataAttribute : System.Attribute
{
//this is custom field given just for an example
//you can create attribute without any fields
//even an empty attribute can be used - as marker
public string Text { get; set; }
//this constructor could be used as [MethodMetadata]
public MethodMetadataAttribute ()
{
}
//This constructor could be used as [MethodMetadata("String")]
public MethodMetadataAttribute (string text)
{
Text = text;
}
}
属性を使用する
[StackDemo(Text = "Hello, World!")]
public class MyClass
{
[StackDemo("Hello, World!")]
static void MyMethod()
{
}
}
属性を読み込む
メソッドGetCustomAttributes
は、メンバーに適用されるカスタム属性の配列を返します。この配列を取得した後、1つまたは複数の特定の属性を検索できます。
var attribute = typeof(MyClass).GetCustomAttributes().OfType<MyCustomAttribute>().Single();
またはそれらを繰り返す
foreach(var attribute in typeof(MyClass).GetCustomAttributes()) {
Console.WriteLine(attribute.GetType());
}
System.Reflection.CustomAttributeExtensions
GetCustomAttribute
拡張メソッドは、指定された型のカスタム属性を取得し、任意のMemberInfo
適用できます。
var attribute = (MyCustomAttribute) typeof(MyClass).GetCustomAttribute(typeof(MyCustomAttribute));
GetCustomAttribute
には、検索する属性の種類を指定するための汎用シグネチャもあります。
var attribute = typeof(MyClass).GetCustomAttribute<MyCustomAttribute>();
ブール引数のinherit
は、これらのメソッドの両方に渡すinherit
ができます。この値をtrue
設定すると、要素の祖先も検査されます。
DebuggerDisplay属性
DebuggerDisplay
属性を追加すると、 DebuggerDisplay
が上に乗ったときにデバッガがそのクラスを表示する方法が変更されます。
{}
囲まれた式は、デバッガによって評価されます。これは、次のサンプルやより複雑なロジックのような単純なプロパティにすることができます。
[DebuggerDisplay("{StringProperty} - {IntProperty}")]
public class AnObject
{
public int ObjectId { get; set; }
public string StringProperty { get; set; }
public int IntProperty { get; set; }
}
閉じ括弧の前,nq
追加すると、文字列を出力するときに引用符が削除されます。
[DebuggerDisplay("{StringProperty,nq} - {IntProperty}")]
{}
では一般的な表現が許可されていますが、推奨されません。 DebuggerDisplay
属性は、アセンブリメタデータに文字列として書き込まれます。 {}
内の式の妥当性はチェックされません。したがって、より複雑なロジックを含むDebuggerDisplay
属性、つまりC#ではうまくいくかもしれませんが、VB.NETで評価された同じ式は構文上有効ではなく、デバッグ中にエラーが発生します。
DebuggerDisplay
を言語に依存しないようにする方法は、メソッドやプロパティに式を記述して代わりに呼び出すことです。
[DebuggerDisplay("{DebuggerDisplay(),nq}")]
public class AnObject
{
public int ObjectId { get; set; }
public string StringProperty { get; set; }
public int IntProperty { get; set; }
private string DebuggerDisplay()
{
return $"{StringProperty} - {IntProperty}"";
}
}
DebuggerDisplay
がプロパティの全部または一部を出力し、オブジェクトのタイプもデバッグして調べることができます。
以下の例は、 DebuggerDisplay
がデバッグ環境で使用されるため、ヘルパーメソッドを#if DEBUG
囲みます。
[DebuggerDisplay("{DebuggerDisplay(),nq}")]
public class AnObject
{
public int ObjectId { get; set; }
public string StringProperty { get; set; }
public int IntProperty { get; set; }
#if DEBUG
private string DebuggerDisplay()
{
return
$"ObjectId:{this.ObjectId}, StringProperty:{this.StringProperty}, Type:{this.GetType()}";
}
#endif
}
発信者情報属性
呼び出し元情報属性を使用して、呼び出し元に関する情報を呼び出されたメソッドに渡すことができます。宣言は次のようになります。
using System.Runtime.CompilerServices;
public void LogException(Exception ex,
[CallerMemberName]string callerMemberName = "",
[CallerLineNumber]int callerLineNumber = 0,
[CallerFilePath]string callerFilePath = "")
{
//perform logging
}
呼び出しは次のようになります。
public void Save(DBContext context)
{
try
{
context.SaveChanges();
}
catch (Exception ex)
{
LogException(ex);
}
}
最初のパラメータのみが明示的にLogException
メソッドに渡され、残りはコンパイル時に関連する値とともに提供されることに注意してください。
callerMemberName
パラメータは、呼び出し元のメソッドの名前である"Save"
という値を受け取り"Save"
。
callerLineNumber
パラメーターは、いずれの行の数受け取るLogException
メソッド呼び出しがで書き込まれます。
また、 'callerFilePath'パラメータはSave
メソッドが宣言されているファイルのフルパスを受け取りSave
。
インタフェースから属性を読み取る
クラスはインタフェースから属性を継承しないため、インタフェースから属性を取得する簡単な方法はありません。インターフェイスを実装したり、派生クラス内のメンバーをオーバーライドする場合は常に、属性を再宣言する必要があります。したがって、以下の例では、3つのケースすべてでTrue
が出力されます。
using System;
using System.Linq;
using System.Reflection;
namespace InterfaceAttributesDemo {
[AttributeUsage(AttributeTargets.Interface, Inherited = true)]
class MyCustomAttribute : Attribute {
public string Text { get; set; }
}
[MyCustomAttribute(Text = "Hello from interface attribute")]
interface IMyClass {
void MyMethod();
}
class MyClass : IMyClass {
public void MyMethod() { }
}
public class Program {
public static void Main(string[] args) {
GetInterfaceAttributeDemo();
}
private static void GetInterfaceAttributeDemo() {
var attribute1 = (MyCustomAttribute) typeof(MyClass).GetCustomAttribute(typeof(MyCustomAttribute), true);
Console.WriteLine(attribute1 == null); // True
var attribute2 = typeof(MyClass).GetCustomAttributes(true).OfType<MyCustomAttribute>().SingleOrDefault();
Console.WriteLine(attribute2 == null); // True
var attribute3 = typeof(MyClass).GetCustomAttribute<MyCustomAttribute>(true);
Console.WriteLine(attribute3 == null); // True
}
}
}
インターフェイス属性を取得する1つの方法は、クラスによって実装されたすべてのインターフェイスを通じてインターフェイス属性を検索することです。
var attribute = typeof(MyClass).GetInterfaces().SelectMany(x => x.GetCustomAttributes().OfType<MyCustomAttribute>()).SingleOrDefault();
Console.WriteLine(attribute == null); // False
Console.WriteLine(attribute.Text); // Hello from interface attribute
廃止された属性
System.Obsoleteは、より良いバージョンを持つ型またはメンバーをマークするために使用される属性であるため、使用しないでください。
[Obsolete("This class is obsolete. Use SomeOtherClass instead.")]
class SomeClass
{
//
}
上記のクラスが使用されている場合、コンパイラは "This class is obsolete。SomeOtherClassを代わりに使用する"という警告を表示します。