Showing posts with label Attributes. Show all posts
Showing posts with label Attributes. Show all posts

Thursday, December 26, 2013

Custom Attributes in C#, Part 2

Intro

Back in Part 1 we looked at what attributes are, a reason or two why you might be interested in using them, and how to get their values through reflection. A bit more work was left to your imagination regarding custom attributes. Now it's time to get yer thinkin cap on (maybe it's a beer hat!) and get crackin!

Create Your Own Custom Attribute

Not only can you use built-in .Net framework attributes, you can also create your own custom attributes. I've used them for property validation, object serialization and storage, and many more things. Basically anywhere you need to describe, or give extra information about a code element, you should at least consider using a custom attribute to do the trick. Here's a silly example:

    public enum SillinessFactor
    {
        SuperSilly,
        KindaSilly,
        NotSoSilly,
    }

    public class SillyAttribute : System.Attribute
    {
        public SillinessFactor SillinessFactor { get; set; }

        public SillyAttribute(SillinessFactor sillinessFactor)
        {
            this.SillinessFactor = sillinessFactor;
        }
    }


In the above code we have created an enum to describe the level of silliness that something represents. Just below that enum we have a class called SillyAttribute that derives from the System.Attribute class. Creating your own custom attribute is as simple as that; just derive from System.Attribute. I decided that I would like users to be able to state the level of silliness when applying the Silly attribute to a piece of code, so I coded a constructor that allows the user to pass in one of the valid SillinessFactor enum values. Using this new custom attribute is just as easy as what you saw in the last lesson:

    [Silly(SillinessFactor.SuperSilly)]
    public class SillyClass
    {
    }


Well lookie there pilgrim! We've got a silly class here who is officially marked as being Silly (through the custom attribute), and is assigned a silliness factor of SuperSilly. Yee haw!

Limiting attributes to specific scope(s) 

It's also possible to limit the application of your custom attributes to specific scope(s). Let's say for example that only classes can be Silly, not properties or anything else. How would we accomplish such a monumental feat?

    [System.AttributeUsage(System.AttributeTargets.Class)]
    public class SillyAttribute : System.Attribute
    {
        public SillinessFactor SillinessFactor { get; set; }

        public SillyAttribute(SillinessFactor sillinessFactor)
        {
            this.SillinessFactor = sillinessFactor;
        }
    }


Oh, sweet irony! Yes you accomplish the limitation of attribute scope through another attribute! In the redone sample above, we've told the compiler to limit the Silly attribute to application on classes only. What happens if you try to stick it on an attribute? I could tell you, but then I'd be spoiling all the fun!

What's Next?

There's really not much left with attributes. Experiment with using some of the built-in ones, google around to see how other people use them, and make your own even if it's just a sample. Who knows, you might find a real-world application after you've cemented this little gem in yer noggin.

Resources

Thursday, December 19, 2013

Custom Attributes in C#, Part 1

Intro, Including Why to Use Them

Attributes are yet another tool in your programming arsenal (hee hee, I said "arse"). If they're not already, we'll fix that right now! What is an attribute? It's a declarative way to associate information (usually called meta-information) with your code. What can you use attributes for? Just about anywhere you want to associate information with your code. Helpful answer huh? Like a judge and jury, I try. Here's a sample that might give you an idea:

    public enum VehicleBody
    {
        [Description("Sedan")]
        Sedan,
        [Description("Coupe")]
        Coupe,
        [Description("Station Wagon")]
        StationWagon,
        [Description("Crossover")]
        Crossover,
        [Description("Mini-van")]
        MiniVan,
    }

Above we have a simple enumeration that represents a few possible vehicle body types. Those attributes above each body type associate a specific user-friendly string with each entry. If we had a GUI driven by this information we could fill a drop-down list with those descriptions as the text, and the enumeration value as the value. Ooh, gotta love foreshadowing...

Example of Using a Built-in .Net Attribute, and Hey Some Reflection!


    
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using BlogAttributes;
using System.Reflection;

namespace BlogWebGui
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                var enumNames = Enum.GetNames(typeof(VehicleBody)).ToList();
                foreach (var enumName in enumNames)
                {
                    var type = typeof(VehicleBody);
                    var member = type.GetMember(enumName)[0];
                    var attribute = member.GetCustomAttribute(typeof(DescriptionAttribute));
                    var bodyDesription = ((DescriptionAttribute)attribute).Description;
                    uxVehicleBody.Items.Add(new ListItem() { Value = enumName, Text = bodyDesription });
                }
            }
        }
    }
}
The first code snippet above shows a rather simple section from an aspx page. Just a dropdownlist here.
The second code snippet is the code-behind for the aspx page. In the page load we populate the dropdownlist with the names and descriptions of our enums. Let's look at the important lines:
  1. "var enumNames..." This line just gets the names of our enumerated type values. All this means is we get a generic list containing the .ToString() representation of all members of our enumeration.
  2. "var member = type.GetMember(enumName)[0];" This sets the value of "member" to the correct value from the enumeration. For example, if enumName is set to "StationWagon", this code sets member to the value VehicleBody.StationWagon.
  3. "var attribute..." Here we set attribute to the attribute placed on the current enumeration value. In this case the attribute is of type DescriptionAttribute.
  4. "var bodyDescription..."Now we're getting the meat of it! Here we take the attribute we got above and grab its Description property to get that actual value we put into the attribute. So if we are looking at [Description("Station Wagon")]StationWagon, then this code returns the value "Station Wagon".
  5. "uxVehicleBody.Items..." Now we add each enumeration name and description attribute value to the dropdown list.
Here's the relevant portion of the html generated by the above code, which should help to further explain what the code is doing:
        


Pretty cool huh? Yeah I know this doesn't use up any less code than creating an array which contains the descriptive strings, but this code does have one large advantage over that approach: with enums and attributes, the lists will not get out of synch. With separate arrays holding values associated with enumerated types it is very easy to get the lists out of synch.

What's Next?

You might be thinking "has Peeticus misled me? I thought this article was about CUSTOM attributes, not using pre-canned ones?". Sorry, I only have so much space on each individual post and my attention span is being artificially shortened by an angry cat. Feel free to peek ahead though! We'll create our own custom attributes next week, I promise.

Resources

  • http://msdn.microsoft.com/en-us/library/aa288454%28v=vs.71%29.aspx