Attributes
Attributes are an extensible mechanism for adding custom information to code elements (assemblies, types, members, return values, parameters, and generic type parameters).
This extensibility is useful for services that integrate deeply into the type system, without requiring special keywords or constructs in the C# language.
Example:
serialization – the process of converting arbitrary objects to and from a particular format.
Attribute Classes
Attribute defined by a class inherits from abstract class System.Attribute.
To attach an attribute to a code element, specify the attribute’s type name in square brackets, before the code element.
[ObsoleteAttribute] public class Foo { ... }
This attribute is recognized by the compiler and will cause compiler warnings if a type or member marked obsolete is referenced.
Named and Positional Attribute Parameters
Attributes may have parameters.
The following attribute maps the CustomerEntity class to an XML element named Customer, belonging to the http://oreilly.com namespace.
[XmlElement ("Customer", Namespace="http://oreilly.com")] public class CustomerEntity { ... }
Attribute parameters fall into one of two categories: positional or named. The first argument is a positional parameter and second is a named parameter.
Positional parameters correspond to parameters of the attribute type’s public constructors. You must include positional parameters that correspond to one of the attribute’s constructors.
Named parameters correspond to public fields or public properties on the attribute type and it is optional.
Attribute Targets
Implicitly, the target of an attribute is the code element it immediately precedes, which is typically a type or type member.
Example of using the CLSCompliant attribute to specify CLS compliance for an entire assembly.
[assembly:CLSCompliant(true)]
Specifying Multiple Attributes
Multiple attributes can be specified for a single code element.
Each attribute can be listed either within the same pair of square brackets (separated by a comma) or in separate pairs of square brackets (or a combination of the two).
[Serializable, Obsolete, CLSCompliant(false)] public class Bar {...} [Serializable] [Obsolete] [CLSCompliant(false)] public class Bar {...} [Serializable, Obsolete] [CLSCompliant(false)] public class Bar {...}
Caller Info Attributes (C# 5)
From C# 5, you can tag optional parameters with one of three caller info attributes, which instruct the compiler to feed information obtained from the caller’s source code into the parameter’s default value:
• [CallerMemberName] applies the caller’s member name
• [CallerFilePath] applies the path to caller’s source code file
• [CallerLineNumber] applies the line number in caller’s source code file
using System.Runtime.CompilerServices; namespace Rextester { public class Program { public static void Main(string[] args) { Foo(); } static void Foo ( [CallerMemberName] string memberName = null, [CallerFilePath] string filePath = null, [CallerLineNumber] int lineNumber = 0) { Console.WriteLine (memberName); Console.WriteLine (filePath); Console.WriteLine (lineNumber); } } }
Output:
Main
c:\Windows\Temp\dlexntz1.0.cs
16
Caller info attributes are useful for logging—and for implementing patterns such as firing a single change notification event whenever any property on an object changes.
INotifyPropertyChanged in System.ComponentModel
public interface INotifyPropertyChanged { event PropertyChangedEventHandler PropertyChanged; } public delegate void PropertyChangedEventHandler (object sender, PropertyChangedEventArgs e); public class PropertyChangedEventArgs : EventArgs { public PropertyChangedEventArgs (string propertyName); public virtual string PropertyName { get; } }
By applying the [CallerMemberName] attribute, however, it implement this interface and invoke the event without ever specifying property names
public class Foo : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged = delegate { }; void RaisePropertyChanged ([CallerMemberName] string propertyName = null) { PropertyChanged (this, new PropertyChangedEventArgs (propertyName)); } string customerName; public string CustomerName { get { return customerName; } set { if (value == customerName) return; customerName = value; RaisePropertyChanged(); // The compiler converts the above line to: // RaisePropertyChanged ("CustomerName"); } } }