17. Attributes

Much of the C# language enables the programmer to specify declarative information about the entities defined in the program. For example, the accessibility of a method in a class is specified by decorating it with the method-modifiers public, protected, internal, and private.

C# enables programmers to invent new kinds of declarative information, called attributes. Programmers can then attach attributes to various program entities, and retrieve attribute information in a runtime environment. For instance, a framework might define a HelpAttribute attribute that can be placed on certain program elements (such as classes and methods) to provide a mapping from those program elements to their documentation.

Attributes are defined through the declaration of attribute classes (§17.1), which may have positional and named parameters (§17.1.2). Attributes are attached to entities in a C# program using attribute specifications (§17.2), and can be retrieved at runtime as attribute instances (§17.3).

17.1 Attribute Classes

A class that derives from the abstract class System.Attribute, whether directly or indirectly, is an attribute class. The declaration of an attribute class defines a new kind of attribute that can be placed on a declaration. By convention, attribute classes are named with a suffix of Attribute. Uses of an attribute may either include or omit this suffix.

17.1.1 Attribute Usage

The attribute AttributeUsage17.4.1) is used to describe how an attribute class can be used.

AttributeUsage has a positional parameter (§17.1.2) that enables an attribute class to specify the kinds of declarations on which it can be used. The example

        using System;
        [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)]
        public class SimpleAttribute: Attribute
        {
                …
        }

defines an attribute class named SimpleAttribute that can be placed on class-declarations and interface-declarations only. The example

        [Simple] class Class1 {…}
        [Simple] interface Interface1 {…}

shows several uses of the Simple attribute. Although this attribute is defined with the name SimpleAttribute, when the attribute is used, the Attribute suffix may be omitted, resulting in the short name Simple. Thus the example above is semantically equivalent to the following:

        [SimpleAttribute] class Class1 {…}
        [SimpleAttribute] interface Interface1 {…}

AttributeUsage has a named parameter (§17.1.2) called AllowMultiple, which indicates whether the attribute can be specified more than once for a given entity. If AllowMultiple for an attribute class is true, then that attribute class is a multi-use attribute class and can be specified more than once on an entity. If AllowMultiple for an attribute class is false or it is unspecified, then that attribute class is a single-use attribute class and can be specified at most once on an entity.

The example

        using System;
        [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
        public class AuthorAttribute: Attribute
        {
                private string name;
                public AuthorAttribute(string name) {
                       this.name = name;
                }
                public string Name {
                       get { return name; }
                }
        }

defines a multi-use attribute class named AuthorAttribute. The example

        [Author("Brian Kernighan"), Author("Dennis Ritchie")]
        class Class1
        {
                …
        }

shows a class declaration with two uses of the Author attribute.

AttributeUsage has another named parameter called Inherited, which indicates whether the attribute, when specified on a base class, is also inherited by classes that derive from that base class. If Inherited for an attribute class is true, then that attribute is inherited. If Inherited for an attribute class is false, then that attribute is not inherited. If it is unspecified, its default value is true.

An attribute class X not having an AttributeUsage attribute attached to it, as in

        using System;
        class X: Attribute {…}

is equivalent to the following:

        using System;
        [AttributeUsage(
                AttributeTargets.All,
                AllowMultiple = false,
                Inherited = true)
        ]
        class X: Attribute {…}

17.1.2 Positional and Named Parameters

Attribute classes can have positional parameters and named parameters. Each public instance constructor for an attribute class defines a valid sequence of positional parameters for that attribute class. Each nonstatic public read-write field and property for an attribute class define a named parameter for the attribute class.

The example

        using System;
        [AttributeUsage(AttributeTargets.Class)]
        public class HelpAttribute: Attribute
        {
                public HelpAttribute(string url) {        // Positional parameter
                     …
                }
                public string Topic {                               // Named parameter
                       get {…}
                       set {…}
                }
                public string Url {
                       get {…}
                }
        }

defines an attribute class named HelpAttribute that has one positional parameter, url, and one named parameter, Topic. Although it is nonstatic and public, the property Url does not define a named parameter, because it is not a read-write property.

This attribute class might be used as follows:

        [Help("http://www.mycompany.com/…/Class1.htm")]
        class Class1
        {
                …
        }
        [Help("http://www.mycompany.com/…/Misc.htm", Topic = "Class2")]
        class Class2
        {
                …
        }

17.1.3 Attribute Parameter Types

The types of positional and named parameters for an attribute class are limited to the following attribute parameter types:

•  One of the following types: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort

•  The type object

•  The type System.Type

•  An enum type, provided it has public accessibility and the types in which it is nested (if any) also have public accessibility (§17.2)

•  Single-dimensional arrays of the above types

If a constructor argument or public field does not have one of these types, it cannot be used as a positional or named parameter in an attribute specification.

17.2 Attribute Specification

Attribute specification is the application of a previously defined attribute to a declaration. An attribute is a piece of additional declarative information that is specified for a declaration. Attributes can be specified at global scope (to specify attributes on the containing assembly or module) and for type-declarations (§9.6), class-member-declarations (§10.1.5), interface-member-declarations (§13.2), struct-member-declarations (§11.2), enum-member-declarations (§14.3), accessor-declarations (§10.7.2), event-accessor-declarations10.8.1), and formal-parameter-lists10.6.1).

Attributes are specified in attribute sections. An attribute section consists of a pair of square brackets, which surround a comma-separated list of one or more attributes. The order in which attributes are specified in such a list, and the order in which sections attached to the same program entity are arranged, is not significant. For instance, the attribute specifications [A][B], [B][A], [A, B], and [B, A] are all equivalent.

    global-attributes:
            global-attribute-sections

    global-attribute-sections:
            global-attribute-section
            global-attribute-sections  global-attribute-section

    global-attribute-section:
            [  global-attribute-target-specifier  attribute-list  ]

            [  global-attribute-target-specifier attribute-list  ,  ]

    global-attribute-target-specifier:
            global-attribute-target  :

    global-attribute-target:
            assembly
            module

    attributes:
            attribute-sections

    attribute-sections:
            attribute-section
            attribute-sections  attribute-section

    attribute-section:
            [  attribute-target-specifieropt attribute-list ]
            [  attribute-target-specifieropt attribute-list  ,  ]

    attribute-target-specifier:
            attribute-target  :

    attribute-target:
            field
            event
            method
            param
            property
            return
            type

    attribute-list:
            attribute
            attribute-list  ,  attribute

    attribute:
            attribute-name  attribute-argumentsopt

    attribute-name:
            type-name

    attribute-arguments:
            (  positional-argument-listopt  )
            (  positional-argument-list  ,  named-argument-list  )
            (  named-argument-list  )

    positional-argument-list:
            positional-argument
            positional-argument-list  ,  positional-argument

    positional-argument:
            attribute-argument-expression

    named-argument-list:
            named-argument
            named-argument-list  ,  named-argument

    named-argument:
            identifier  =  attribute-argument-expression

    attribute-argument-expression:
            expression

An attribute consists of an attribute-name and an optional list of positional and named arguments. The positional arguments (if any) precede the named arguments. A positional argument consists of an attribute-argument-expression; a named argument consists of a name, followed by an equal sign, followed by an attribute-argument-expression, which together are constrained by the same rules as simple assignment. The order of the named arguments is not significant.

The attribute-name identifies an attribute class. If the form of attribute-name is type-name, then this name must refer to an attribute class. Otherwise, a compile-time error occurs. The example

        class Class1 {}
        [Class1] class Class2 {}        // Error

results in a compile-time error because it attempts to use Class1 as an attribute class when Class1 is not an attribute class.

Certain contexts permit the specification of an attribute on more than one target. A program can explicitly specify the target by including an attribute-target-specifier. When an attribute is placed at the global level, a global-attribute-target-specifier is required. In all other locations, a reasonable default is applied, but an attribute-target-specifier can be used to affirm or override the default in certain ambiguous cases (or to just affirm the default in non-ambiguous cases). As a consequence, attribute-target-specifiers can typically be omitted, except at the global level. The potentially ambiguous contexts are resolved as follows:

•  An attribute specified at global scope can apply either to the target assembly or to the target module. No default exists for this context, so an attribute-target-specifier is always required in this case. The presence of the assembly attribute-target-specifier indicates that the attribute applies to the target assembly; the presence of the module attribute-target-specifier indicates that the attribute applies to the target module.

•  An attribute specified on a delegate declaration can apply either to the delegate being declared or to its return value. In the absence of an attribute-target-specifier, the attribute applies to the delegate. The presence of the type attribute-target-specifier indicates that the attribute applies to the delegate; the presence of the return attribute-target-specifier indicates that the attribute applies to the return value.

•  An attribute specified on a method declaration can apply either to the method being declared or to its return value. In the absence of an attribute-target-specifier, the attribute applies to the method. The presence of the method attribute-target-specifier indicates that the attribute applies to the method; the presence of the return attribute-target-specifier indicates that the attribute applies to the return value.

•  An attribute specified on an operator declaration can apply either to the operator being declared or to its return value. In the absence of an attribute-target-specifier, the attribute applies to the operator. The presence of the method attribute-target-specifier indicates that the attribute applies to the operator; the presence of the return attribute-target-specifier indicates that the attribute applies to the return value.

•  An attribute specified on an event declaration that omits event accessors can apply to the event being declared, to the associated field (if the event is not abstract), or to the associated add and remove methods. In the absence of an attribute-target-specifier, the attribute applies to the event. The presence of the event attribute-target-specifier indicates that the attribute applies to the event; the presence of the field attribute-target-specifier indicates that the attribute applies to the field; and the presence of the method attribute-target-specifier indicates that the attribute applies to the methods.

•  An attribute specified on a get accessor declaration for a property or indexer declaration can apply either to the associated method or to its return value. In the absence of an attribute-target-specifier, the attribute applies to the method. The presence of the method attribute-target-specifier indicates that the attribute applies to the method; the presence of the return attribute-target-specifier indicates that the attribute applies to the return value.

•  An attribute specified on a set accessor for a property or indexer declaration can apply either to the associated method or to its lone implicit parameter. In the absence of an attribute-target-specifier, the attribute applies to the method. The presence of the method attribute-target-specifier indicates that the attribute applies to the method; the presence of the param attribute-target-specifier indicates that the attribute applies to the parameter; and the presence of the return attribute-target-specifier indicates that the attribute applies to the return value.

•  An attribute specified on an add or remove accessor declaration for an event declaration can apply either to the associated method or to its lone parameter. In the absence of an attribute-target-specifier, the attribute applies to the method. The presence of the method attribute-target-specifier indicates that the attribute applies to the method; the presence of the param attribute-target-specifier indicates that the attribute applies to the parameter; and the presence of the return attribute-target-specifier indicates that the attribute applies to the return value.

In other contexts, inclusion of an attribute-target-specifier is permitted but unnecessary. For instance, a class declaration may either include or omit the specifier type:

        [type: Author("Brian Kernighan")]
        class Class1 {}
        [Author("Dennis Ritchie")]
        class Class2 {}

It is an error to specify an invalid attribute-target-specifier. For instance, the specifier param cannot be used on a class declaration:

        [param: Author("Brian Kernighan")]        // Error
        class Class1{}

By convention, attribute classes are named with a suffix of Attribute. An attribute-name of the form type-name may either include or omit this suffix. If an attribute class is found both with and without this suffix, an ambiguity is present, and a compile-time error results. If the attribute-name is spelled such that its rightmost identifier is a verbatim identifier (§2.4.2), then only an attribute without a suffix is matched, enabling such an ambiguity to be resolved. The example

        using System;
        [AttributeUsage(AttributeTargets.All)]
        public class X: Attribute
        {}
        [AttributeUsage(AttributeTargets.All)]
        public class XAttribute: Attribute
        {}
        [X]                            // Error: ambiguity
        class Class1 {}
        [XAttribute]           // Refers to XAttribute
        class Class2 {}
        [@X]                        // Refers to X
        class Class3 {}
        [@XAttribute]        // Refers to XAttribute
        class Class4 {}

shows two attribute classes named X and XAttribute. The attribute [X] is ambiguous, as it could refer to either X or XAttribute. Using a verbatim identifier allows the exact intent to be specified in such rare cases. The attribute [XAttribute] is not ambiguous (although it would be if an attribute class named XAttributeAttribute existed!). If the declaration for class X is removed, then both attributes refer to the attribute class named XAttribute.

        using System;
        [AttributeUsage(AttributeTargets.All)]
        public class XAttribute: Attribute
        {}
        [X]                               // Refers to XAttribute
        class Class1 {}
        [XAttribute]               // Refers to XAttribute
        class Class2 {}
        [@X]                             // Error: no attribute named "X"
        class Class3 {}

It is a compile-time error to use a single-use attribute class more than once on the same entity. The example

        using System;
        [AttributeUsage(AttributeTargets.Class)]
        public class HelpStringAttribute: Attribute
        {
                string value;
                public HelpStringAttribute(string value) {
                     this.value = value;
                }
                public string Value {
                     get {…}
                }
        }
        [HelpString("Description of Class1")]
        [HelpString("Another description of Class1")]
        public class Class1 {}

results in a compile-time error because it attempts to use HelpString, which is a single-use attribute class, more than once on the declaration of Class1.

An expression E is an attribute-argument-expression if all of the following statements are true:

•  The type of E is an attribute parameter type (§17.1.3).

•  At compile-time, the value of E can be resolved to one of the following:

-  A constant value

-  A System.Type object

-  A one-dimensional array of attribute-argument-expression

For example,

        using System;
        [AttributeUsage(AttributeTargets.Class)]
        public class TestAttribute: Attribute
        {
                public int P1 {
                     get {…}
                     set {…}
                }
                public Type P2 {
                     get {…}
                     set {…}
                }
                public object P3 {
                     get {…}
                     set {…}
                }
        }
        [Test(P1 = 1234, P3 = new int[] {1, 3, 5}, P2 = typeof(float))]
        class MyClass {}

A typeof-expression7.5.11) used as an attribute argument expression can reference a non-generic type, a closed constructed type, or an unbound generic type, but it cannot reference an open type. This behavior ensures that the expression can be resolved at compile time.

        class A: Attribute
        {
                public A(Type t) {…}
        }
        class G<T>
        {
                [A(typeof(T))] T t;                                // Error, open type in attribute
        }
        class X
        {
                [A(typeof(List<int>))] int x;               // Okay, closed constructed type
                [A(typeof(List<>))] int y;                    // Okay, unbound generic type
        }

17.3 Attribute Instances

An attribute instance is an instance that represents an attribute at runtime. An attribute is defined with an attribute class, positional arguments, and named arguments. An attribute instance is an instance of the attribute class that is initialized with the positional and named arguments.

Retrieval of an attribute instance involves both compile-time and runtime processing, as described in the following sections.

17.3.1 Compilation of an Attribute

The compilation of an attribute with attribute class T, positional-argument-list P, and named-argument-list N, consists of the following steps:

•  Follow the compile-time processing steps for compiling an object-creation-expression of the form new T(P). These steps either result in a compile-time error or determine an instance constructor C on T that can be invoked at runtime.

•  If C does not have public accessibility, then a compile-time error occurs.

•  For each named-argument Arg in N:

-  Let Name be the identifier of the named-argument Arg.

-  Name must identify a nonstatic read-write public field or property on T. If T has no such field or property, then a compile-time error occurs.

•  Keep the following information for runtime instantiation of the attribute: the attribute class T, the instance constructor C on T, the positional-argument-list P, and the named-argument-list N.

17.3.2 Runtime Retrieval of an Attribute Instance

Compilation of an attribute yields an attribute class T, an instance constructor C on T, a positional-argument-list P, and a named-argument-list N. Given this information, an attribute instance can be retrieved at runtime using the following steps:

•  Follow the runtime processing steps for executing an object-creation-expression of the form new T(P), using the instance constructor C as determined at compile time. These steps either result in an exception or produce an instance O of T.

•  For each named-argument Arg in N, in order:

-  Let Name be the identifier of the named-argument Arg. If Name does not identify a non-static public read-write field or property on O, then an exception is thrown.

-  Let Value be the result of evaluating the attribute-argument-expression of Arg.

-  If Name identifies a field on O, then set this field to Value.

-  Otherwise, Name identifies a property on O. Set this property to Value.

-  The result is O, an instance of the attribute class T that has been initialized with the positional-argument-list P and the named-argument-list N.

17.4 Reserved Attributes

A small number of attributes affect the language in some way:

•  System.AttributeUsageAttribute17.4.1) is used to describe the ways in which an attribute class can be used.

•  System.Diagnostics.ConditionalAttribute17.4.2) is used to define conditional methods.

•  System.ObsoleteAttribute17.4.3) is used to mark a member as obsolete.

17.4.1 The AttributeUsage Attribute

The attribute AttributeUsage is used to describe the manner in which the attribute class can be used.

A class that is decorated with the AttributeUsage attribute must derive from System.Attribute, either directly or indirectly. Otherwise, a compile-time error occurs.

        namespace System
        {
                [AttributeUsage(AttributeTargets.Class)]
                public class AttributeUsageAttribute: Attribute
                {
                     public AttributeUsageAttribute(AttributeTargets validOn) {…}
                     public virtual bool AllowMultiple { get {…} set {…} }
                     public virtual bool Inherited { get {…} set {…} }
                     public virtual AttributeTargets ValidOn { get {…} }
                }
                public enum AttributeTargets
                {
                    Assembly                =  0x0001,
                     Module                   =  0x0002,
                     Class                      =  0x0004,
                     Struct                     =  0x0008,
                     Enum                      =  0x0010,
                     Constructor          =  0x0020,
                     Method                   =  0x0040,
                     Property                =  0x0080,
                     Field                       =  0x0100,
                     Event                      =  0x0200,
                     Interface               =  0x0400,
                     Parameter            =  0x0800,
                     Delegate               =  0x1000,
                     ReturnValue       =  0x2000,
                   All = Assembly | Module | Class | Struct | Enum | Constructor |
                            Method | Property | Field | Event | Interface | Parameter |
                            Delegate | ReturnValue
                }
        }

17.4.2 The Conditional Attribute

The attribute Conditional enables the definition of conditional methods and conditional attribute classes.

        namespace System.Diagnostics
        {
                [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,
                                            AllowMultiple = true)]
                public class ConditionalAttribute: Attribute
                {
                       public ConditionalAttribute(string conditionString) {…}
                       public string ConditionString { get {…} }
                }
        }

17.4.2.1 Conditional Methods

A method decorated with the Conditional attribute is a conditional method. The Conditional attribute indicates a condition by testing a conditional compilation symbol. Calls to a conditional method are either included or omitted depending on whether this symbol is defined at the point of the call. If the symbol is defined, the call is included; otherwise, the call (including evaluation of the parameters of the call) is omitted.

A conditional method is subject to the following restrictions:

•  The conditional method must be a method in a class-declaration or struct-declaration. A compile-time error occurs if the Conditional attribute is specified on a method in an interface declaration.

•  The conditional method must have a return type of void.

•  The conditional method must not be marked with the override modifier. A conditional method may be marked with the virtual modifier, however. Overrides of such a method are implicitly conditional, and they must not be explicitly marked with a Conditional attribute.

•  The conditional method must not be an implementation of an interface method. Otherwise, a compile-time error occurs.

In addition, a compile-time error occurs if a conditional method is used in a delegate-creation-expression. The example

        #define DEBUG
        using System;
        using System.Diagnostics;
        class Class1
        {
                [Conditional("DEBUG")]
                public static void M() {
                       Console.WriteLine("Executed Class1.M");
                }
        }
        class Class2
        {
                public static void Test() {
                       Class1.M();
                }
        }

declares Class1.M as a conditional method. Class2’s Test method calls this method. Because the conditional compilation symbol DEBUG is defined, if Class2.Test is called, it will call M. If the symbol DEBUG had not been defined, then Class2.Test would not call Class1.M.

It is important to note that the inclusion or exclusion of a call to a conditional method is controlled by the conditional compilation symbols at the point of the call. In the example

File class1.cs:

        using System.Diagnostics;
        class Class1
        {
                [Conditional("DEBUG")]
                public static void F() {
                       Console.WriteLine("Executed Class1.F");
                }
        }

File class2.cs:

        #define DEBUG
        class Class2
        {
                public static void G() {
                       Class1.F();                            // F is called
                }
        }

File class3.cs:

        #undef DEBUG
        class Class3
        {
                public static void H() {
                       Class1.F();                        // F is not called
                }
        }

each of the classes Class2 and Class3 contains a call to the conditional method Class1.F, which is conditional based on whether DEBUG is defined. Because this symbol is defined in the context of Class2 but not Class3, the call to F in Class2 is included, while the call to F in Class3 is omitted.

The use of conditional methods in an inheritance chain can be confusing. Calls made to a conditional method through base, of the form base.M, are subject to the normal conditional method call rules. For example,

File class1.cs:

        using System;
        using System.Diagnostics;
        class Class1
        {
                [Conditional("DEBUG")]
                public virtual void M() {
                       Console.WriteLine("Class1.M executed");
                }
        }

File class2.cs:

        using System;
        class Class2: Class1
        {
                public override void M() {
                       Console.WriteLine("Class2.M executed");
                       base.M();                                                  // base.M is not called!
                }
        }

File class3.cs:

        #define DEBUG
        using System;
        class Class3
        {
                public static void Test() {
                       Class2 c = new Class2();
                       c.M();                                                       // M is called
                }
        }

Here, Class2 includes a call to the M defined in its base class. This call is omitted because the base method is conditional based on the presence of the symbol DEBUG, which is undefined. Thus the method writes to the console “Class2.M executed” only. Judicious use of pp-declarations can eliminate such problems.

17.4.2.2 Conditional Attribute Classes

An attribute class (§17.1) decorated with one or more Conditional attributes is a conditional attribute class. A conditional attribute class is, therefore, associated with the conditional compilation symbols declared in its Conditional attributes. The example

        using System;
        using System.Diagnostics;
        [Conditional("ALPHA")]
        [Conditional("BETA")]
        public class TestAttribute : Attribute {}

declares TestAttribute as a conditional attribute class associated with the conditional compilations symbols ALPHA and BETA.

Attribute specifications (§17.2) of a conditional attribute are included if one or more of the attribute’s associated conditional compilation symbols is defined at the point of specification. Otherwise, the attribute specification is omitted.

The inclusion or exclusion of an attribute specification of a conditional attribute class is controlled by the conditional compilation symbols at the point of the specification. In the example

File test.cs:

        using System;
        using System.Diagnostics;
        [Conditional("DEBUG")]
        public class TestAttribute : Attribute {}

File class1.cs:

        #define DEBUG
        [Test]                        //    TestAttribute is specified
        class Class1 {}

File class2.cs:

        #undef DEBUG
        [Test]                        //    TestAttribute is not specified
        class Class2 {}

each of the classes Class1 and Class2 is decorated with the attribute Test, which is conditional based on whether DEBUG is defined. Because this symbol is defined in the context of Class1 but not Class2, the specification of the Test attribute on Class1 is included, while the specification of the Test attribute on Class2 is omitted.

17.4.3 The Obsolete Attribute

The attribute Obsolete is used to mark types and members of types that should no longer be used.

        namespace System
        {
                [AttributeUsage(
                       AttributeTargets.Class |
                       AttributeTargets.Struct |
                         AttributeTargets.Enum |
                       AttributeTargets.Interface |
                       AttributeTargets.Delegate |
                       AttributeTargets.Method |
                       AttributeTargets.Constructor |
                       AttributeTargets.Property |
                       AttributeTargets.Field |
                       AttributeTargets.Event,
                       Inherited = false)
                ]
                public class ObsoleteAttribute: Attribute
                {
                       public ObsoleteAttribute() {…}
                       public ObsoleteAttribute(string message) {…}
                       public ObsoleteAttribute(string message, bool error) {…}
                       public string Message { get {…} }
                       public bool IsError { get {…} }
                }
        }

If a program uses a type or member that is decorated with the Obsolete attribute, the compiler will issue a warning or an error. Specifically, the compiler will issue a warning if no error parameter is provided, or if the error parameter is provided and has the value false. The compiler will also issue an error if the error parameter is specified and has the value true.

In the example

        [Obsolete("This class is obsolete; use class B instead")]
        class A
        {
                public void F() {}
        }
        class B
        {
                public void F() {}
        }
        class Test
        {
                static void Main() {
                       A a = new A();                // Warning
                       a.F();
                }
        }

the class A is decorated with the Obsolete attribute. Each use of A in Main results in a warning that includes the specified message, “This class is obsolete; use class B instead.”

17.5 Attributes for Interoperation

Note: This section is applicable only to the Microsoft .NET implementation of C#.

17.5.1 Interoperation with COM and Win32 Components

The .NET runtime provides a large number of attributes that enable C# programs to interoperate with components written using COM and Win32 DLLs. For example, the DllImport attribute can be used on a static extern method to indicate that the implementation of the method can be found in a Win32 DLL. These attributes are found in the System.Runtime.InteropServices namespace, and detailed documentation for these attributes is found in the .NET runtime documentation.

17.5.2 Interoperation with Other .NET Languages

Indexers are implemented in .NET using indexed properties and have names in the .NET metadata. If no IndexerName attribute is present for an indexer, then the name Item is used by default. The IndexerName attribute enables a developer to override this default and specify a different name.

        namespace System.Runtime.CompilerServices.CSharp
        {
                [AttributeUsage(AttributeTargets.Property)]
                public class IndexerNameAttribute: Attribute
                {
                       public IndexerNameAttribute(string indexerName) {…}
                       public string Value { get {…} }
                }
        }

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset