C# Language
Атрибуты
Поиск…
Создание настраиваемого атрибута
//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
возвращает массив пользовательских атрибутов, применяемых к элементу. После извлечения этого массива вы можете искать один или несколько конкретных атрибутов.
var attribute = typeof(MyClass).GetCustomAttributes().OfType<MyCustomAttribute>().Single();
Или итерации через них
foreach(var attribute in typeof(MyClass).GetCustomAttributes()) {
Console.WriteLine(attribute.GetType());
}
GetCustomAttribute
расширения GetCustomAttribute
из System.Reflection.CustomAttributeExtensions
извлекает пользовательский атрибут указанного типа, его можно применить к любому MemberInfo
.
var attribute = (MyCustomAttribute) typeof(MyClass).GetCustomAttribute(typeof(MyCustomAttribute));
GetCustomAttribute
также имеет общую подпись для указания типа атрибута для поиска.
var attribute = typeof(MyClass).GetCustomAttribute<MyCustomAttribute>();
inherit
логических аргументов может быть передано обоим этим методам. Если для этого значения установлено значение true
предки элемента также будут проверяться.
Атрибут 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
может выводить все или только некоторые из свойств, а также при отладке и проверке типа объекта.
Пример ниже также окружает вспомогательный метод с #if DEBUG
поскольку DebuggerDisplay
используется в средах отладки.
[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"
- имя вызывающего метода.
Параметр callerLineNumber
будет получать количество строк, на которые LogException
вызов метода LogException
.
И параметр 'callerFilePath' получит полный путь к файлу. Объявлен метод Save
.
Чтение атрибута из интерфейса
Нет простого способа получить атрибуты из интерфейса, поскольку классы не наследуют атрибуты от интерфейса. Всякий раз, когда вы выполняете интерфейс или переопределяете члены производного класса, вам нужно повторно объявить атрибуты. Таким образом, в приведенном ниже примере вывод будет 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
}
}
}
Одним из способов получения атрибутов интерфейса является поиск их через все интерфейсы, реализованные классом.
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
{
//
}
В случае использования класса выше компилятор выдаст предупреждение «Этот класс устарел. Вместо этого используйте SomeOtherClass».