Introduction

In this article I hope to show you what attributes are, how to use existing
attributes, and how to create your own attributes to use in your own projects.

What this article assumes you know or have

You can read C# code

You have some version of the .NET framework installed, my demo program
uses the final release so you'll have to recompile if you have an earlier
version.

What are attributes

Essentially attributes are a means of decorating your code with various
properties at compile time. This can be passive, such as marking a
class as Serializable
with the SerializableAttribute, or it can take a more active
role, such as the MarshalAsAttribute
which tells the runtime how to pass data
between managed and unmanaged code.

In the former case when you try serializing an object, the runtime checks to
see if the object has the SerializableAttribute applied to it. In this
case the attribute is nothing more than a flag to let the runtime know that the
classes author gave the OK for serialization (since this could expose some
sensitive data to someone who should see it).

In the latter case a more active role is taken. The MarshalAsAttribute
when applied to a field in a struct will tell the runtime what type of data the
.NET type should be formatted as when it is sent to unmanaged code, and what
type it should convert the return value from when it comes back from the unmanaged code.

void MyMethod([MarshalAs(LPStr)] string s);

The above method marshals the string as an LPStr (zero terminated ANSI
string), the string is modified by the runtime so that it can be used by the
function MyMethod.

All attributes inherit from System.Attribute
and are classes just like 90% of
the framework. This also means that what you can do with a class you can
do with an attribute; given an instance of an attribute you can get its
underlying type. You can also create attributes at runtime with the Reflection.Emit classes and bind them to classes you have built with the Emit
package.

How to use attributes

Simple Attributes

In C# attributes are placed before the item that they will be "decorating"
and they are put inside square braces [].

[Serializable()]
publicclass MySerializableClass
....

Like C#, VB.NET places attributes before the item that will be decorated,
except VB.NET uses angle brackets <>.

<Serializable()>
PublicClass MySerializableClass
....

Notice that even though the real name for the attribute is SerializableAttribute we didn't write Attribute? The only time that you
can leave off the Attribute portion of the name is when you are applying the
Attribute to a class

The code above actually expands out to

[SerializableAttribute()]
publicclass MySerializableClass

This shortened name can only be used when the attribute is being applied, in
your code that uses the attribute you must use the full name.

Note here I had to give the full name just as I would with any other class.

More complex attributes

The constructor for MarshalAs is defined as public
MarshalAsAttribute(UnmanagedType);. Note that it only accepts one
parameter, what about the other properties?

This is the most confusing part about attributes so you may have to do a
couple examples before this part clicks since it deviates from normal C style
methods and gets into a VB-esque style.

You set the properties in the constructor, after all the parameters for the
constructor. For example, we're going to pass an array of three 32-bit
ints to a method, I'll omit the actual void MyMethodgarbage since you
can see that above

I don't want to weigh you down with the specifics of MarshalAs
since I just
want to use it as an example, but you can see that the first thing passed in was
a member of the UnmanagedType enum, followed by setting of two properties
SizeConst and ArraySubType. This is how all attributes will
work, if the constructor doesn't allow you to set the property, you can set it
by doing so after the parameters of the constructor.

VB programmers will remember this as passing by name.

Creating your own attributes

Ok, you've seen how to use an attribute, how about making one?

If you're using VS.NET create a new console project called TestAttribute and
remove any files that are added to it, we'll be replacing them with our own
versions. If you are using the .NET Framework SDK just create a new
directory to house the project.

Project Guidelines

The attribute we will create here will have an imaginary importance in our
demo project. Let's say that it serves as a flag for our program to do
something with the class it is applied to, the number signifying what method of
testing to use. We also wish to have the class name itself, but this isn't
required.

There will be two test classes, one with the default name, and the other
specifying its name.

The driver class will search for our attribute on objects passed in. If
the attribute exists it will printout the classname and call the attributes
PrintOut method. If the attribute doesn't exist, it will print out a
message saying that it doesn't exist.

Open up a new file called TestAttribute.cs (create a new file called
TestAttribute.cs if you're using VS.NET) and put the following code into it

Thats all there is to your most basic of attributes, data is defined on
it and a single method is there to print out the values of those fields.. As you can see its just a class that inherits from System.Attribute,
and it has two public fields, these could have easily been properties if we
wanted, but my focus was on as little code as possible.

Now lets go use it!

TestClasses.cs

Create a file called TestClasses.cs (add a new class called TestClasses if you're
using VS.NET) and add the following code.

Now compile the program on the command line by running csc like so csc /out:test.exe /target:exe *.cs

When you execute the program you should get output like the image at the top
shows.

How it works

All of the work is done by the framework with the call to GetCustomAttributes
on the Type object. GetCustomAttributes takes two parameters, the first is
a Type object for the attribute we wish to get, the second is a boolean telling
the framework whether it should look at the types that this class derives from.
GetCustomAttributes returns an object array containing each of the attributes
found that match the Type passed in. In this case we requested all
attributes of a specific type, so we can safely cast that to an array of our
attribute. At the very least we could cast that to an array of
Attribute's. If you wish to get all attributes attached to a type
you just pass in value for the bool.

If you look up the GetCustomAttributes method you'll see that it is defined
on the MemberInfo
class. This class is an abstract class which has several
derivatives; Type, EventInfo, MethodBase, PropertyInfo, and
FieldInfo.
Each of those classes represents a part of the very basics for the type system.
Right now the TestAttribute
can be applied to all those parts of a class, what
if TestAttribute
only makes sense to be applied to classes? Enter the
AttributeUsage attribute.

Once it has gotten an array of TestAttribute's
it determines how many are in
the array; if there are none in the array we know that the class doesn't have
the TestAttribute
applied to it; else we take the first one in the array and
tell it to output its values by the PrintOut
method we created in the TestAttribute class.

AttributeUsage Attribute

This attribute is used at compile time to ensure that attributes can only be
used where the attribute author allows. This attribute also allows the
author to specify whether the same attribute can be applied multiple times to
the same object and if the attribute applies to objects that derive from the
class it is applied to.

A simple modification to TestAttribute.cs restricts our attribute to being
applied only to classes.

Now the TestAttribute can only be applied to classes. By default
attributes aren't inherited and can only be applied once so we needn't do
anything more with it.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]

The usage above would allow the attribute to be applied to only classes and
structs.

Below is the list of all the flags in the AttributeTargets
enumeration.
The enumeration is marked as Flags so you can combine two or more different
values to allow the attribute to be applied to more than one element.

AttributeTargets

All

Assembly

Class

Constructor

Delegate

Enum

Event

Field

Interface

Method

Module (this refers to a .NET executable
file, not a VB module)

Parameter

Property

ReturnValue

Struct

Want a brain teaser? Look at the documentation for the AttributeUsage
attribute, notice that it too has the AttributeUsage
attribute applied to it...
So which came first? The AttributeUsage
attribute or the AttributeUsage
attribute!

Summary

Attributes can be attached to virtually everything, classes, methods, fields,
properties, parameters in methods, return values of methods; having at least a
basic knowledge of what attributes are and how to use them is extremely helpful
in the long run. In reading this article you should have learned how to
use them as well as create basic attributes that you can use in your own code.

Share

About the Author

James has been programming in C/C++ since 1998, and grew fond of databases in 1999. His latest interest has been in C# and .NET where he has been having fun writing code starting when .NET v1.0 was in its first beta.

He is currently a senior developer and consultant for InfoPlanIT, a small international consulting company that focuses on custom solutions and business intelligence applications.

He was previously employed by ComponentOne where he was a Product Manager for the ActiveReports, Data Dynamics Reports, and ActiveAnalysis products.

Code contained in articles where he is the sole author is licensed via the new BSD license.

Thanks for a great article, very concise and clear. I have just started using attributes pretty extensively and thiw was very helpful.

Do you know of a way to define and apply attributes at RunTime. I am extending a control in VB.Net and have serialized certain properties of that control so that the developer can configure their own default settings. But the DefaultValue attribute only takes a constant value. I have been trying to figure out a way to apply them after the fact to the configuarable properties so that the Property browser at DesignTime properly shows their choices unbolded.

Any ideas or suggestions would be greatly welcomed. Feel free to contact me at the signature line below. Thanks again for a nice article!

The designer will look for some specially named methods in your Form/Control class to allow you to do this type of thing: void ResetMyProperty() and bool ShouldSerializeMyProperty(). This applies to both .NET 1.x and 2.0

ShouldSerialize should return true in the event that the current value is not equal to the value the property will have upon control creation. Reset will be called when the user chooses the reset option on the context menu. That option will only be available when ShouldSerialize returns true.

Now if you are really intent on using attributes, and are able to use .NET 2.0 read on...

In .NET 2.0 you can inherit from the DefaultValueAttribute class to change how it works. [In .NET 1.x the DefaultValueAttribute class is sealed, and thus you can't inherit from it]

Rather than pass in a constant value representing the default; you could pass in a value to uniquely identify the control (for example the form name and name of the control).

Now if you use your new attribute instead of the one from the framework you should get your defaults to show up. I don't know how well this will play with the designer though; for example if your default changes while the control is loaded in the designer the new default may not show until the form is reopened...or worse when the solution is closed and reopened.

Thanks for your reply, it was helpful and just repointed me in the direction I need to go. I was already using ResetPropName and ShouldSerializePropName elsewhere in the project.

The biggest headache I was having was getting properties based on enumerations to work. It appears that <> with enumerations is inconsistent, at least within Reset and ShouldSerialize. When I replaced them with the following code they began to work:

I'm planning to use Authorization and Profile Application Block for my application's access control. Access check need to be performed in each method call to ensure user has the right permission, like:

Is it possible to use attributes to control whether a class inherited from Form appears in designer? I have a base class which inherits from Form and implements only non-visual functionality. Therefore, I want to be able to edit the code in the IDE editor but not in the designer. And, to make things more complicated, I don't want this restriction for forms that inherit from the base class.

I have written an exported DLL that I call from C#. Some of the methods take/return a SAFEARRAY *. The DLL creates the SafeArray using SafeArrayCreateVectorEX. My DLLImport statement specifies the following

and I am declaring the return type as a byte []. When the function returns, it appears that I have a valid byte array. But who cleans up the SAFEARRAY*? Does the marshaller take care of this? Or do I somehow need to specify to the DLL to destroy the SAFEARRAY*?

Along the same lines, my DLL takes BSTR parameters. I have been converting my strings to IntPtr using Marshal.StringToPTr method, I pass in the IntPtr, and when I return, I use Marshal.FreeBSTR to clean up the IntPtr. This way, I know I am cleaning up the BSTR. If I were to delcare my method specifying to MarshalAs BSTR, so that I won't have to do the conversion manually, who would clean up that BSTR? The Marshaller, or would I have to call SysFreeString on the inside of the DLL?!

I have looked around the internet and the Help files for an answer but to no avail. Micro$oft just doesn't specify where the clean up must be done and by who?

Okay, it is possible to match an AttributeUsage only to fields with the help of AttributeTargets enum. But what can I do to fix a special attribute only on fields which data type is for instance a string? Is it possible? Have you a piece of code to verify that?

If you want the attribute to be applied to object Types that aren't sealed (ie it is legal to create a new class deriving from the class you want to restrict the Attribute to) then you should replace fi.MemberType == typeof(string) with fi.MemberType.IsInstanceOfType( typeof(MyClass) ). This allows the attribute to be applied to fields of type MyClass as well as MyDerivedClass.

HTH,

James

At Jethro Tull's August 28, 2003 concert Ian Anderson mentioned that the group would be performing a medley of title tracks. The songs were "Songs from the Wood", "Too Old to Rock and Roll; Too Young to Die"; and from the Heavy Horses album, "Stairway to Heaven".

Doing so might not be easy, but it can change the debugging from run-time to compile-time.

James

At Jethro Tull's August 28, 2003 concert Ian Anderson mentioned that the group would be performing a medley of title tracks. The songs were "Songs from the Wood", "Too Old to Rock and Roll; Too Young to Die"; and from the Heavy Horses album, "Stairway to Heaven".

I found your article extremely informatiive and useful. I was on the lookout for an article that could explain custom Attributes. However, I came across an error when I started applying it into my VB.NET project. I'm trying to marshal an array from .NET To ASP and wanted to apply a MarshalAsAttribute to the variable array that I declared. Following is my definition of the variable:

The above definition gives me a compile time error, "Constant Expression is Required" and it underlines "SafeArraySubType".I have imported the System.Runtime.InteropServices.VarEnum and also the System.Runtime.InteropServices.MarshalAsAttribute classes. Any clues on how I could get this working would be of great help.

Notice that between the property name and the value you use :=, not just = as C# uses. This tells VB that the argument named 'SafeArraySubType' is to receive the value of 'VarEnum.VT_VARIANT' and leave the other arguments at their default value.

Here is a point where C# and VB differ, C# doesn't allow method parameters to have default values, so they created the extended syntax where you act like you are setting a property. VB on the other hand DOES allow method parameters to have default values so it treats Attributes as having one (or several) large constructors with default parameters. Hence, why you need to use the named parameters method.

HTH,

James

At Jethro Tull's August 28, 2003 concert Ian Anderson mentioned that the group would be performing several songs in a row and that they were all title tracks. The songs were "Songs from the Wood", "Too Old to Rock and Roll; Too Young to Die"; and from the Heavy Horses album, "Stairway to Heaven"

Thanks so much for the response. I actually have been struggling for an entire day to get this syntax right!.

I have yet another issue, I'm actually working on an interop project and trying to marshal arrays from VB.NET To ASP. Thats where I'm explicitly using these attributes. I'm getting type mismatch if I use the SafeArray with the MarshalAs Attribute and on the other hand I'm able to retrieve the array values in VB if I use LPArray. But there seems to be a distinctive problem, early binding in VB does not show the functions that return the array, and thats becuase Regasm /tlb utility generates warnings for Arrays marshalled using LPArray and ASP, although retrieves Array, but is unable to loop through and returns type mismatch if looped through.

Any ideas how I could marshal an array of objects from VB.NET To ASP and to VB.

I don't know how I missed the e-mail notification about your reply, but I hope a little late is better than never

I'm not well versed in the ins and outs of COM Interop so I think it would be better for me to bow out than mislead you. Stephane Rodriguez used to have an article that I think was pretty good about explaining some of the aspects of it, but now I can't find it

James

At Jethro Tull's August 28, 2003 concert Ian Anderson mentioned that the group would be performing a medley of title tracks. The songs were "Songs from the Wood", "Too Old to Rock and Roll; Too Young to Die"; and from the Heavy Horses album, "Stairway to Heaven".

Type.GetCustomAttributes will only return the attributes defined on the type member you used it on.

So when you call it on the Type representing a class it will return the attributes applied to the class. When you call it on the MethodInfo object representing a particular method, it will return the attributes applied to that method.

To retreive them all you can use a recursive method to get them all. A short example follows, as compiled by CodeProject (in other words, I'm winging it so there may be syntax or minor logic errors):

To use this, you pass in the CustomAttributeInstance obtained from the GetCustomAttributes method above as well as the instance of the class* and the new value.

Well, I didn't plan on writing all this, but here it is anyway

* I'll let you figure out how you should determine what to pass in for the instance, I can tell you that an instance of the parent type won't work here, you will probably have to modify GetCustomAttributes to return a hierarchial collection instead of a flat one

James

"It is self repeating, of unknown pattern"Data - Star Trek: The Next Generation

You really can't, Attributes are used to describe meta-data which is embedded in the assembly at compile time. Because it is embedded at compile-time attributes are essentially constants, until the time (if ever) that .NET allows user-attributes to inject code into the assmblies the attributes are used on.

Patrick Foo wrote:I got a property with an 'ReadOnly' attribte.Right now, this property is displayed onto the PropertyGrid Control in a window form.

Its a lot more involved than you are currently doing, but you can use PropertyDescriptor's (and its IsReadOnly property) to achieve what you want to do. However I'm not familiar at all with how you need to go about doing this, but I think there is a CodeProject article which describes it. Here it is: Bending the .NET PropertyGrid to Your Will[^].

Hope that helps,

James

"It is self repeating, of unknown pattern"Data - Star Trek: The Next Generation

Hi, I've been trying to work out a way of implementing the equivalent of C++ templates to define standard datatypes in VB.Net.I have a situation where I want to create strongly typed ArrayLists for about 10 different class of objects, and the suggested method is to either wrap or subclass the ArrayList class, but this seems to be an awful waste of coding simply to enforce a cast on the Object parameter of 10 methods and properties Is there any way the Attributes could be used to override the datatype of parameters inside a class, or am I barking up the wrong tree?

iGadget wrote:Is there any way the Attributes could be used to override the datatype of parameters inside a class, or am I barking up the wrong tree?

At the moment you are looking at the wrong solution. In order for this to work well you would need the compiler to specifically look for your attributes so it can generate the proper code. So you're still looking at waiting for generics to be provided by .NET (possibly in version 2.0).

Meanwhile, I highly suggest using Chris Sells' CollectionGen[^] for creating any custom collections you need. It has support for a Vector (ArrayList substitute), Hashtable, and SortedList.

Even better is that the Vector doesn't use the ArrayList, so you don't incur boxing penalties for ValueTypes.

I've just started to develop in .NET and I have run into a problem I just don't know how to solve:I use the Directory class to do various things, for instance Directive::Exists, and this works fine, but the compiler will not accept Directive::CreateDirective. The error message is: error C2039: 'CreateDirectoryA': is not a member of 'System::IO::Directory'.

At first I thought it had to do with the character set so I changed from Multi-Byte to Unicode but that did not help (the error message was then that "CreateDirectoryW" not was a member). My OS is Windows NT 4.0 sp6, but I have tried to compile my project on XP as well with the same result. Directory.CreateDirectory works fine in VB.

Another problem I have, that I think is related is that the compiler doesn't recognise MessageBox as a member of 'System::Windows::Forms'.

The problem you are seeing is that you have included some windows header files; which use #defines to make it so that you get the Ansi or Wide character versions of a function depending on whether UNICODE is defined.

To get around that problem, you need to undefine the macro somewhere in that source file before you use the CreateDirectory or MessageBox method/class.

I am writing a code generation tool that generates a new class based on existing classes. I use reflection to get the class members, and I want to copy all the existing attributes to the new class. However I can not get the attribute constructor information correct in the copied attributes. Any help or leads would be great.

Hello James, very good article. Can you give any information on how a Custom Attribute could reflect on the class (item) it decorates? I am working on a Unit testing framework and I want to be able to store the test data AND generic test in a custom attribute class. Then, the testing application will ask the attribute to perform and report all tests. Reflection is needed so the custom attribute can decide if every property/method has a corresponding test and every test has a property/method. Also, reflection will alow the test to cascade down the class tree structue as the custom attribute will be able to construct custom attributes on all members.

In MyAttribute.DoWork you would walk the stack to get to the instance that called the method. Ed Stegman posted code to do this for a custom attribute he wrote to make fixed length strings; however I can't find it in the DOTNET archives.

I'm not familiar with Unit Testing so you'll have to forgive any ignorance, but the same technique my article uses could still apply. In other words, at runtime you do a GetCustomAttributes on the MemberInfo type you are looking for (Type, FieldInfo, PropertyInfo, MethodInfo, etc). Then you do the same where else it is needed.

One thing that looks interesting is John Lam's Runtime Aspect Weaver. It will allow you to "weave" code in and out at runtime so you could put in some blanket code, "every time I call DoWork() I want it to call System.Diagnostics.Trace.Write("Output", theValue);".

Appologies for not responding sooner, since you posted Anonymously I didn't get an e-mail notifying me.

I think the SerializableAttribute is a special case because the compiler interprets it as setting a bit in the meta-data for the class. I would assume that you could still check to see if it is applied but I don't even see how the Serialization mechanism checks for its existance.

Your second problem is easier though

What you are looking for is the MarshalAsAttribute being applied to the type; it isn't. It is applied to a field. So you need to do something like this.

Yes of course, that was my fault. I sent you the wrong test program. I was testing your solution as well and it does not work. I added as well the attribute FieldAttribut, witch work well. But ma_attributes.Length is always 0!

I am currently writing a Custom Attribute which can be used to serialize a class to Xml. The CA can be attached to the class and the properties inside the class. It looks something like this

<AttributeUsage (AttributeTargets.Class Or AttributeTargets.Property)> Public Class XmlModifier
Inherits System.Attribute
Private m_tXmlNode As System.Type ' defines the node type (XmlElement, XmlAttribute)
Private m_strNodeName As String ' xpath to the parent node
Private m_strParentXPath As String' name of this node
<p>
[...] ' following New and readonly-properties for the members above
End Class

What I want to do, is to call a function, such as DynCreateXml(), which parses the properties and builds up an Xml Document:

<root><Element1AnAttribute="123"/></root>

But I want to retrieve all CA's of a class in one single step. I found that GetCustomAttributes() needs detailed type information. But I don't see the sense to get CA's of a type I already know. Or am I wrong?

The other thing is, that I don't understand how to retrieve CA's assigned to a property.

Michael Groeger wrote:But I want to retrieve all CA's of a class in one single step.

There are two overloaded versions of GetCustomAttributes(), one that takes a type and a bool the other takes just the bool.

If you want to retreive all CA's attached to a class call it like this myType.GetCustomAttributes(bSearchClassHierarchy).

Michael Groeger wrote:But I don't see the sense to get CA's of a type I already know.

Its really just a matter of efficiency. If you are only looking for a specific CA then passing in the CA's type makes sense; but if you are going to look for more than one CA it might be smarter to get all CA's then handle just the ones you want.

In order for a CA to be useful some code *somewhere* has to be written to do something with it specifically. This can happen at the compiler level (SerializableAttribute), at the framework level (MarshalAs), or in your own code (XmlModifier).

Michael Groeger wrote:I don't understand how to retrieve CA's assigned to a property.

James T. Johnson wrote:Michael Groeger wrote:But I want to retrieve all CA's of a class in one single step.

There are two overloaded versions of GetCustomAttributes(), one that takes a type and a bool the other takes just the bool.

If you want to retreive all CA's attached to a class call it like this myType.GetCustomAttributes(bSearchClassHierarchy).

Oh I see. Okay, this will help me out.

James T. Johnson wrote:Michael Groeger wrote:But I don't see the sense to get CA's of a type I already know.

Its really just a matter of efficiency. If you are only looking for a specific CA then passing in the CA's type makes sense; but if you are going to look for more than one CA it might be smarter to get all CA's then handle just the ones you want.

Ok. Of course you're right. My point of view to attributes might have been delimited to my current situation, which afforded to get all attributes attached to a class.

James T. Johnson wrote:Michael Groeger wrote:I don't understand how to retrieve CA's assigned to a property.

My VB is a bit rusty but I think this snippet will do it.

Thankyou for the example. As the class library is for all the same, I think you've also could have written some pseudo-code, which just uses the correct libs For instance, I would have no problem if you are writing c# as I am normally a c++ developer and only the current project affords using vb.

James T. Johnson wrote:Now for another topic completely.

Did you know that MS already did most of what you're trying to do for you?

Check out the XmlSerializer in the System.Xml.Serialization namespace.

Hehe. I tried this already and it looks quite good. But what I don't like is that you have to build your classes the way the Xml should look alike. So I think, I will try to expand the XmlSerializer to match my issues.

Hi there,I understand that Microsoft has openeed up everything they have to offer in terms of classes, reflection, emit etc and exposed all the programming blocks. Similarly they have opened the Attributes also. I understand that, with Attribute class, you can keep creating, deleting attributes on the fly. That is where a technical feasibility is attained.

Buy, what I'm interested is that, why they have given so many attibutes and extending the attributes, what benefits you are going to get.

What are the practical kind of application are we looking at to apply these dynamic attributes?

Anonymous wrote:I understand that, with Attribute class, you can keep creating, deleting attributes on the fly.

Much of what I have seen that does that is in the Xml.Serialization namespace of classes. If you want you can override some of the serializers settings so that data is handled differently. But I wouldn't call this a dynamic application of attributes since the XmlSerializer looks for the XmlAttributeOverrides property specifically, rather than a class having an attribute at one point and not having it at another point during execution.

Anonymous wrote:extending the attributes, what benefits you are going to get.

By creating your own attributes you are really just letting yourself re-use existing code.

In one of my projects I created a set of attributes and classes that serialized a class to a database, where every field and property could correspond to a field in the database. The attributes specified the Field name, its datatype and length, whether it was nullable or a primary key, and where it should be added as a parameter to the DbCommand object.

My DbSerializer class would then build a list of these attributes and when calling its serialize function it would fetch the values, then add them to the command object.

I wrote this because everytime I was writing a class that represented an object in the database I was writing pretty much the same code everytime for saving/loading to/from the database.

Many of the attributes in the CLR are just there so that you can use code over again. The MarshalAs attribute tells the PInvoke functions how to change the layout of your data so that you don't have to everytime you use PInvoke. The Serializable attribute sets a bit in the metadata for your class that tells the runtime your class is serializable.

Some of the attributes tell the compiler how to handle something, such as the Serializable attribute, many of the Assembly and module attributes do the same.

In Visual Studio Magazine there is an article that shows how you can use attributes to create a Windows Form that will modify its conents based on the class passed in.

Question one...Why [Test(3)]? Isn't Test the name of the namespace? I'm not clear on that.

Question Two...I assume [Test(4, Name = "TestClassB")] initializes the Attribute. Suppose the attribute had two ints instead of an int and string as properties/public fields. What would that piece of code look like?

Senkwe Chanda wrote:Question one...Why [Test(3)]? Isn't Test the name of the namespace? I'm not clear on that.

Test is the name of the namespace (and I probably should've chosen a better name), but because we're applying the attribute the framework will automatically add Attribute to the name for us. So [Test(3)] is really [TestAttribute(3)].

Senkwe Chanda wrote:Question Two...I assume [Test(4, Name = "TestClassB")] initializes the Attribute. Suppose the attribute had two ints instead of an int and string as properties/public fields. What would that piece of code look like?

That depends on what constructors were available on the TestAttribute class. If you have a constructor that takes the two ints, then you should use that.

If you don't have a constructor available then you set each field/property at the end of the constructor but still inside the ). [Test(3, MySecondInt = 5, MyDouble = 3.05634)].

At first it seems really odd to set properties and fields inside of the constructor, especially when the only constructor defined takes a single integer as its only parameter. But after working with it for a little bit it seems almost second nature now.

HTH,

James

Sonork ID: 100.11138 - Hasaki"My words but a whisper -- your deafness a SHOUT. I may make you feel but I can't make you think." - Thick as a Brick, Jethro Tull 1972