Attributes
Attributes
Fundamentals of Attributes
Introduction
In the .NET Framework programming, an attribute is a piece of information that accompanies a class-type (such as a class, an enumeration, a structure, an interface, a generic type, etc) or a member (a field, a property, a method, etc). There are various reasons this accompaniment is done or why and when it is necessary. In many cases, an attribute is optional; that is, the attribute can be omitted. In some cases, an attribute is either required or extremely necessary so much that if it is missing or omitted, the class is practically incomplete or it can hardly accomplish its purpose.
In some cases, an attribute is primarily used to simply describe a class-type or a member. In some other cases, an attribute is used to indicate to the compiler how it should treate an object created from the class-type or how it should behave when it accesses the member on which the attribute is applied.
Creating an Attribute
To lay a common foundation for all attributes, the .NET Framework provides a special class named Attribute. There are rules you must follow to create an attribute:
public class Observance : Attribute
{
}
The Attribute class is filled with members that the child classes need to get all types of information about an attribute or the class that uses it. Like any regular class, the class of an attribute, also called an attribute class, can have any member you want, such as a field, a property, a method. Of course, each member should be created as public so it can be accessed outside the class. The members can be of any type you juge necessary. Here is an example of an attribute class with a property:
public class Observance : Attribute
{
public int VersionNumber { get; set; }
}
You can also include one or more constructors if you want.
Applying Attributes
Introduction
To get the effect(s) of an attrribute, you must apply it to the intended object (class-type) or member. First, you must know the namespace in which the attribute is defined, and then you must include that namespace in the list of namespaces of your code file.
The formula to apply an attribute is:
[attribute-definition] class-or-member
The attribute is included between [ and ]. Each attribute has a name that follows the rules and suggestions of names of classes. Also, the name of every attribute ends with Attribute. Using or omitting this suffix produces the same result.
Depending on its type, there are two primary ways to apply an attribute.
Name-Based Attributes
For some attributes, only the name is used to apply it. That name holds all the information the compiler needs. This means that the primary way you can apply an attribute is to include only its name between [ and ]. Here is an Example:
using System;
public class Observance : Attribute
{
}
[Observance]
public class MembershipApplicant
{
public long AccountNumber { get; set; }
public string FullName { get; set; }
public MembershipApplicant(long nbr, string name)
{
FullName = name;
AccountNumber = nbr;
}
}
Since your class is a normal one, if you need an object from it, you can declare a variable of its type. Here is an example:
using System; using static System.Console; public class Observance : Attribute { } [Observance] public class MembershipApplicant { public long AccountNumber { get; set; } public string FullName { get; set; } public MembershipApplicant(long nbr, string name) { FullName = name; AccountNumber = nbr; } } public class Exercise { static int Main() { MembershipApplicant member = new MembershipApplicant(920485, "Julius Weuer"); WriteLine("Library Membership"); WriteLine("Membership Information"); WriteLine("Account #: {0}", member.AccountNumber); WriteLine("Full Name: {0}", member.FullName); WriteLine("================================="); return 0; } }
This would produce:
Library Membership Membership Information Account #: 920485 Full Name: Julius Weuer ================================= Press any key to continue . . .
An attribute, or the same attribute, can be applied to different objects of various types, including interfaces, classes, structures, properties, constructors, methods, etc. Here are examples:
using System; public class Observance : Attribute { } [Observance] struct WebsiteHost { } [Observance] interface IReliability { } [Observance] public class MembershipApplicant { public long AccountNumber { get; set; } public string FullName { get; set; } public MembershipApplicant(long nbr, string name) { FullName = name; AccountNumber = nbr; } } public class Independance { [Observance] DateTime Memmorable; MembershipApplicant member; [Observance] public Independance() { member = new MembershipApplicant(920485, "Julius Weuer"); } }
The Constructors of an Attribute
An attribute class is primarily a class like any other. This means that if you don't create a constructor in your attribute class, it would automatically get a default constructor. As a result, when applying an attribute by name, you can add empty parentheses. The above attribute can be applied as follows:
using System; public class Observance : Attribute { } [Observance()] struct WebsiteHost { } [Observance()] interface IReliability { } [Observance()] public class MembershipApplicant { public long AccountNumber { get; set; } public string FullName { get; set; } public MembershipApplicant(long nbr, string name) { FullName = name; AccountNumber = nbr; } } public class Independance { [Observance()] DateTime Memmorable; MembershipApplicant member; [Observance()] public Independance() { member = new MembershipApplicant(920485, "Julius Weuer"); } }
In your attribute class, you can add a constructor that takes one or more parameters. Here is an example:
public class Observance : Attribute
{
public int VersionNumber { get; set; }
public string Identifier { get; set; }
public Observance(int vers, string id)
{
Identifier = id;
VersionNumber = vers;
}
}
From your knowledge of C#, remember that if you create a class that has no default constructor but you create at least one constructor that has at least one parameter, the default constructor disappears. As a result, you cannot apply an attribute for such an attribute class with only its name or its name and empty parentheses. You would receive an error.
Function-Based Attributes
Imagine you create an attribute class that includes one or more members. Here is an example of an attribute class with property members of different types:
using System;
public enum Completion { Beginning, Processing, Finalized }
public class Observance : Attribute
{
public int VersionNumber { get; set; }
public string Identifier { get; set; }
public Completion Status { get; set; }
}
Some attributes need an external value in order to be applied. Such an attribute takes an argument. To pass one or more arguments to an attribute, behave as when calling a constructor that takes one or more arguments. This means that the second technique to apply an attribute is to use one of its constructors. This technique works even if the attribute has no constructor.
To use this technique, first type the name of the attribute followed by parentheses. As you are probably aware already when calling a method that takes an argument, C# supports two techniques by which an argumentative method is called:
using System; public class Manager : Attribute { private int lvl; public Manager(int levelOfAuthority) { lvl = levelOfAuthority; } public int Authority { get { return lvl; } } } public class Organization { [Manager(1)] public Organization() { } [Manager(25)] public Organization(string name, int effective) { } }An attribute class can have a constructor that takes more than one parameter. Such parameters are referred to as positional parameters. When applying that attribute, in its parentheses, type a value for each parameter in the order the parameters appear in the parentheses of the constructor. If you are planning to use different numbers of arguments for each call, you can create different constructors. Here are examples:
using System; public enum Completion { Beginning, Processing, Finalized } public class Observance : Attribute { public int VersionNumber { get; set; } public string Identifier { get; set; } public Completion Status { get; set; } public Observance(int nbr) { VersionNumber = nbr; Identifier = "Observance"; Status = Completion.Beginning; } public Observance(int vers, string id) { Identifier = id; VersionNumber = vers; Status = Completion.Beginning; } public Observance(int vers, string id, Completion status) { Identifier = id; Status = status; VersionNumber = vers; } } public class MembershipApplicant { [Observance(1)] public string FullName { get; set; } [Observance(1, "Account #")] public long AccountNumber { get; set; } public MembershipApplicant(long nbr, string name) { FullName = name; AccountNumber = nbr; } } [Observance(36, "Parrallel", Completion.Processing)] struct Composition { }Of course as an alternative, you can create one constructor that takes a parameter for each property and then you can/should (must) provide a default value for each parameter. This can be done as follows:
public class Observance : Attribute
{
public int VersionNumber { get; set; }
public string Identifier { get; set; }
public Completion Status { get; set; }
public Observance(int vers = 1,
string id = "Observance",
Completion status = Completion.Beginning)
{
Identifier = id;
Status = status;
VersionNumber = vers;
}
}
using System; public enum Completion { Beginning, Processing, Finalized } public class Observance : Attribute { public int VersionNumber { get; set; } public string Identifier { get; set; } public Completion Status { get; set; } } [Observance(Status = Completion.Processing)] public class MembershipApplicant { [Observance(Identifier="Member Name:", VersionNumber = 2)] public string FullName { get; set; } public DateTime ApplicationDate { get; set; } [Observance(Status = Completion.Beginning, Identifier = "Account #")] public long AccountNumber { get; set; } public MembershipApplicant(long nbr, string name) { FullName = name; AccountNumber = nbr; } [Observance(Identifier = "Official Call", Status = Completion.Beginning, VersionNumber = 24)] public MembershipApplicant Create() { MembershipApplicant mbr = new MembershipApplicant(18, "Addplication for Admission"); mbr.ApplicationDate = new DateTime(2017, 02, 12); return mbr; } }
Combining Positional and Named Parameters of an Attribute
In your attribute class, you can create a constructor that takes at least one parameter and has one or more properties in the class. In fact, the parameter of the constructor may or may not be related to (one of) the property(properties) of the class. Here is an example of such a class:
using System;
public enum Technique { Training, HelpFile, Online }
public class Documentation : Attribute
{
public int HelpIndex { get;set;}
public string Summary { get; set;}
public Technique Means { get; set;}
public Documentation(string decription)
{
}
public Documentation(string decription, bool started)
{
}
}
For this type of attribute whose class doesn't include a default constructor but has at least one constructor that takes at least one positional parameter, when applying this type of attribute, in its parentheses, first provide the value(s) of the parameter(s). If the constructor of the attribute class includes a constructor that takes more than one parameter, provide the value of each parameter based on their positions. After the arguments of the constructor(s), if you want to specify the value of a property, first add a comma. To provide the value of a property, type its name and assign the desired value using the techniques we saw. Here are examples:
using System; public enum Technique { Training, HelpFile, Online } public class Documentation : Attribute { public int HelpIndex { get;set;} public string Summary { get; set;} public Technique Means { get; set;} public Documentation(string decription) { } public Documentation(string decription, bool started) { } } public class Formulaire { [Documentation("Central Form", Means = Technique.HelpFile, HelpIndex = 1)] public Formulaire(string title, int characteristic) { } public void Present() { } } [Documentation("Camera Picture", true, HelpIndex = 12, Means = Technique.Training)] public class Picture { [Documentation("Camera Picture", false, HelpIndex = 33, Means = Technique.Training, Summary = "Information must include camera and GPS coordinates")] public int Representation { get; set; } } public class Containment { [Documentation("Container Construction", HelpIndex = 6)] public Containment() { } }
Instead of creating various constructors that take different parameters, you can create one that takes each necessary parameter and provide a default value for each. After doing this, when applying the attribute, you can still access each or more than one parameter by their positions. Here is an example:
public class Documentation : Attribute
{
public int HelpIndex { get;set;}
public string Summary { get; set;}
public Technique Means { get; set;}
public Documentation(string decription="Documentation", bool started=false)
{
}
}
The rest of the code works as previously.
An Assembly-Level Attribute
You can apply an attribute to an assembly level. To do this, precede the name of the attribute with assembly:, then followed by the name of the atribute or the attribute and its argument(s).
Applying many Attributes to Something
If necessary, and because attributes have different roles, you can apply more than one attribute to a class or a member. Each attribute definition must be included in its own brackets. The formula to follow is:
[attribute-definition-1] [attribute-definition-2] [attribute-definition ... n] class-or-member
Here are examples:
using System; public enum Completion { Beginning, Processing, Finalized } public class Observance : Attribute { public int VersionNumber { get; set; } public string Identifier { get; set; } public Completion Status { get; set; } } public class Evaluation : Attribute { public OperatingSystem OS { get; set; } public string Processor { get; set; } } public class Localization : Attribute { } [Observance] public class MembershipApplicant { public long AccountNumber { get; set; } public string FullName { get; set; } [Observance] [Evaluation] public MembershipApplicant(long nbr, string name) { FullName = name; AccountNumber = nbr; } } public class Examination { MembershipApplicant member; [Evaluation] [Observance] public Examination() { member = new MembershipApplicant(920485, "Julius Weuer"); } [Evaluation] [Localization] [Observance] public Examination(string caption, float size) { } }
Of course, each attribute can provide its own value(s) and the same attribute applied in various places can provide different values. Here are examples:
using System; public enum Completion { Beginning, Processing, Finalized } public class Observance : Attribute { public int VersionNumber { get; set; } public string Identifier { get; set; } public Completion Status { get; set; } } public class Evaluation : Attribute { public string Platform { get; set; } public string Processor { get; set; } } public class Localization : Attribute { public string Language { get; set; } } [Observance] public class MembershipApplicant { public long AccountNumber { get; set; } public string FullName { get; set; } [Observance(Identifier = "Membership Admission")] [Evaluation(Processor="486")] public MembershipApplicant(long nbr, string name) { FullName = name; AccountNumber = nbr; } } public class Examination { MembershipApplicant member; [Evaluation(Platform = "Microsoft Windows")] [Observance(Status = Completion.Processing, VersionNumber = 4, Identifier="Constructor")] public Examination() { member = new MembershipApplicant(920485, "Julius Weuer"); } [Evaluation(Processor = "8-Core")] [Localization(Language = "US English")] [Observance(VersionNumber = 1, Identifier = "Formula")] public Examination(string caption, float size) { } }
Applying an Attribute to a Class Attribute
Practically every class can be applied an attribute. This includes a class that itself is made for attributes. Here is an example:
public class GPSInformation : Attribute
{
}
[GPSInformation]
public class Statuc : Attribute
{
}
Introduction to Built-In Attributes
An Attribute for a Specific Target
No attribute is made for every scenariot. When creating an attribute class, you may want to specify the type of object (class, property, method, etc) on which the attribute can be applied. To help you specify this information, the .NET Framework provides an enumeration named AttributeTargets.
Combining the Values of an Attribute
As we saw already, an attribute may need to apply more than one value to its target. One way to do this is to combine the values of an attribute as if performing an addition. One version of this type of operation is the bitwise addition. To make this possible, the .NET Framework provides a special attribute class named Flags (or FlagsAttribute). This attribute is usually applied to an enumeration or an enumeration-based property. Here is an example:
[Flags]
public enum MessageButtonIcon
{
OK = 1,
Yes = 2,
No = 3,
Cancel = 4,
Information = 8,
Question = 13,
Help = 22
}
When applied to a property, the type of the property is usually an enumeration. The Flags attribute indicates that the value of the member consists of one or a combination of the members of the enumeration using the bitwise operator "|".
The Usage Attribute
The .NET Framework provides a sealed attribute class named AttributeUsage:
public sealed class AttributeUsageAttribute : Attribute
This class allows you to control many aspects of an attribute class. To use it, apply it to a class you are creating.
The AttributeUsageAttribute class is equipped with one constructor. It takes one argument of type AttributeTargets. As a result, when creating a new attribute class, you should specify the type of object on which the attribute will be applied. To do this, call the constructor of the AttributeUsageAttribute class and pass a member of the AttributeTargets enumeration. The members of this enumeration are:
[AttributeUsage(AttributeTargets.Class)]
public class Attitude : Attribute
{
}
[Attitude]
public class Friend
{
}
using System; using System.Drawing; using System.Windows.Forms; [AttributeUsage(AttributeTargets.Struct)] public class Inclusion : Attribute { } [Inclusion] public struct Survey { }After specifying that an attribute can be applied to only a specific type, if you try applying it on another type, you would receive an error. Here is an example:
[AttributeUsage(AttributeTargets.Struct)] public class Inclusion : Attribute { } [Inclusion] public struct Survey { } [Inclusion] public class Bread { }
That code would produce an error as "error CS0592: Attribute 'Inclusion' is not valid on this declaration type. It is only valid on 'struct' declarations."
The AttributeTargets enumeration is declared as follows:
[SerializableAttribute]
[FlagsAttribute]
[ComVisibleAttribute(true)]
public enum AttributeTargets
As you can see, the AttributeTargets enumeration is marked with the Flags attribute. As a result, you can bitwise combine its members to indicate on what types the attribute can be applied. Here is an example:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class Inclusion : Attribute
{
}
// This will work
[Inclusion]
public class Friend
{
}
// This will work
[Inclusion]
public struct Dog
{
}
// This will fail
[Inclusion]
interface IBird
{
}
Duplicating an Applied Attribute
As mentioned previously, you can apply many attributes to a target. In fact, you can apply the same attribute many times to the same object. This would be done as follows:
public class CommandExecution : Attribute
{
}
public class Assassination
{
[CommandExecution]
[CommandExecution]
public int MustBeCarried { get; set; }
}
Actually, you can control whether an attribute can be applied to an object more than once. To support this, the AttributeUsage class provides its child classes with a Boolean property named AllowMultiple:
public bool AllowMultiple { get; set; }
The default value of this property is false, in which case an attribute can be applied only once to an object. This causes the above code to produce an error. If you want to make it possible for an attribute to be applied more than once, access this property and assign true to it. Here is an example:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] public class CommandExecution : Attribute { } public class Assassination { [CommandExecution] [CommandExecution] public int MustBeCarried { get; set; } }
If you want to apply the attribute as a function, each instance can apply different values. Here are examples:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] public class CommandExecution : Attribute { public string Priority { get; set; } public int Replication { get; set; } public float SpeedTransmission { get; set; } } public class Assassination { [CommandExecution(Priority = "High")] [CommandExecution(SpeedTransmission = 12.68f, Replication = 5)] public int MustBeCarried { get; set; } }
Inheritance
You can create an attribute class that itself is based on a class that either was based directly from the Attribute class or was derived from a class that descended from the Attribute class.
When creating an attribue class, you can control whether other attribute classes can be derived from it. To support this, the AttributeUsageAttribute class is equipped with a Boolean property named Inherited:
public bool Inherited { get; set; }
If you assign true to this property, new attribute classes can be derived from your attribute class. Here is an example:
[AttributeUsage(AttributeTargets.Class, Inherited = true)] public class DialogBehavior : Attribute { } [AttributeUsage(AttributeTargets.Method)] public class Annotation : DialogBehavior { }
If you assign false to this property, no attribute class can be derived from your attribute class.
A Review of Built-In Attributes
Introduction
The .NET Framework provides a very large number of attributes so much that there will hardly be any reason you would want to create your own (or a new) attribute class. This means that almost all of the types of attributes you will need have already been created. You just need to know what attributes are available, what they do, and when (and where) you can (or need to) apply them.
Serializing an Object
Serializing an object consists of saving it to a medium. To make this operation easy, the .NET Framework provides an a named Serializable. Here is an example of starting it:
[Serializable]
public class Tribe
{
public string Country;
public long Population;
}
Marking an Object as Obsolete
Any library, like the .NET Framework, is constantly being revised or updated. In such revisions, sometimes a new and better version of a class-type (an interface, a class, or a structure) or a member of a class-type is created, making the previous version outdated. The previous version of the class-type or the previous version of the member is said to be obsolete. In this case, the old version should not be used anymore. To let you mark a class-type or a member of a class-type as obsolete, the .NET Framework provide an attribute named Obsolete.
The Obsolete attribute indicates that the class-type or the member should not be used anymore. If the class-type or member is used, this attribute asks the compiler to display a warning. To indicate the warning message that would be displayed, pass a string to this attribute. Here is an example:
using System;
public class Formula
{
public Formula()
{
}
[Obsolete("The use of this constructor is not recommended any more.")]
public Formula(int width, int height)
{
}
}
|
||
Previous | Copyright © 2010-2019, FunctionX | Next |
|