C# Language
Atrybuty
Szukaj…
Tworzenie niestandardowego atrybutu
//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;
}
}
Korzystanie z atrybutu
[StackDemo(Text = "Hello, World!")]
public class MyClass
{
[StackDemo("Hello, World!")]
static void MyMethod()
{
}
}
Odczytywanie atrybutu
Metoda GetCustomAttributes
zwraca tablicę niestandardowych atrybutów zastosowanych do elementu członkowskiego. Po pobraniu tej tablicy możesz wyszukać jeden lub więcej określonych atrybutów.
var attribute = typeof(MyClass).GetCustomAttributes().OfType<MyCustomAttribute>().Single();
Lub iteruj przez nie
foreach(var attribute in typeof(MyClass).GetCustomAttributes()) {
Console.WriteLine(attribute.GetType());
}
Metoda rozszerzenia GetCustomAttribute
z System.Reflection.CustomAttributeExtensions
pobiera niestandardowy atrybut określonego typu, można go zastosować do dowolnego MemberInfo
.
var attribute = (MyCustomAttribute) typeof(MyClass).GetCustomAttribute(typeof(MyCustomAttribute));
GetCustomAttribute
ma również podpis ogólny, który określa typ atrybutu do wyszukania.
var attribute = typeof(MyClass).GetCustomAttribute<MyCustomAttribute>();
inherit
argumentu logicznego można przekazać do obu tych metod. Jeśli ta wartość jest ustawiona na true
przodkowie elementu również będą sprawdzani.
DebuggerDisplay Atrybut
Dodanie atrybutu DebuggerDisplay
zmieni sposób, w jaki debugger wyświetla klasę po najechaniu kursorem.
Wyrażenia opakowane w {}
zostaną ocenione przez debugger. Może to być prosta właściwość, jak w poniższym przykładzie lub bardziej złożona logika.
[DebuggerDisplay("{StringProperty} - {IntProperty}")]
public class AnObject
{
public int ObjectId { get; set; }
public string StringProperty { get; set; }
public int IntProperty { get; set; }
}
Dodanie ,nq
przed nawiasem zamykającym usuwa cudzysłowy podczas wyprowadzania łańcucha.
[DebuggerDisplay("{StringProperty,nq} - {IntProperty}")]
Mimo że wyrażenia ogólne są dozwolone w {}
nie są zalecane. Atrybut DebuggerDisplay
zostanie zapisany w metadanych zestawu jako ciąg. Wyrażenia w {}
nie są sprawdzane pod kątem ważności. Zatem atrybut DebuggerDisplay
zawierający bardziej złożoną logikę niż np. Jakaś prosta arytmetyka może działać dobrze w języku C #, ale to samo wyrażenie ocenione w VB.NET prawdopodobnie nie będzie poprawne pod względem składniowym i spowoduje błąd podczas debugowania.
Sposobem na uczynienie DebuggerDisplay
bardziej DebuggerDisplay
od języka jest napisanie wyrażenia w metodzie lub właściwości i wywołanie go zamiast tego.
[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}"";
}
}
Można chcieć, aby DebuggerDisplay
wyświetlał wszystkie lub tylko niektóre właściwości, a podczas debugowania i sprawdzania także typu obiektu.
Poniższy przykład otacza również metodę pomocniczą #if DEBUG
ponieważ DebuggerDisplay
jest używany w środowiskach debugujących.
[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
}
Atrybuty informacji o dzwoniącym
Atrybuty informacji o abonencie wywołującym można wykorzystać do przekazania informacji o wywoływaczu do wywoływanej metody. Deklaracja wygląda następująco:
using System.Runtime.CompilerServices;
public void LogException(Exception ex,
[CallerMemberName]string callerMemberName = "",
[CallerLineNumber]int callerLineNumber = 0,
[CallerFilePath]string callerFilePath = "")
{
//perform logging
}
I wywołanie wygląda następująco:
public void Save(DBContext context)
{
try
{
context.SaveChanges();
}
catch (Exception ex)
{
LogException(ex);
}
}
Zauważ, że tylko pierwszy parametr jest jawnie przekazywany do metody LogException
, podczas gdy reszta zostanie dostarczona w czasie kompilacji z odpowiednimi wartościami.
Parametr callerMemberName
otrzyma wartość "Save"
- nazwę metody wywołującej.
Parametr callerLineNumber
otrzyma numer linii, na LogException
zapisywane jest wywołanie metody LogException
.
A parametr „callerFilePath” otrzyma pełną ścieżkę do pliku, w którym zapisana jest metoda Save
.
Odczytywanie atrybutu z interfejsu
Nie ma prostego sposobu uzyskania atrybutów z interfejsu, ponieważ klasy nie dziedziczą atrybutów z interfejsu. Za każdym razem, gdy implementujesz interfejs lub zastępujesz elementy w klasie pochodnej, musisz ponownie zadeklarować atrybuty. Zatem w poniższym przykładzie dane wyjściowe byłyby True
we wszystkich trzech przypadkach.
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
}
}
}
Jednym ze sposobów pobierania atrybutów interfejsu jest wyszukiwanie ich przez wszystkie interfejsy zaimplementowane przez klasę.
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
Przestarzały atrybut
System.Obsolete jest atrybutem używanym do oznaczania typu lub elementu, który ma lepszą wersję i dlatego nie powinien być używany.
[Obsolete("This class is obsolete. Use SomeOtherClass instead.")]
class SomeClass
{
//
}
W przypadku użycia powyższej klasy kompilator wyświetli ostrzeżenie „Ta klasa jest przestarzała. Zamiast tego użyj SomeOtherClass”.