Ever thought it’d be convenient to attach some extra info to your code? Not just documentation to read at design time, but something that can actually be consumed at runtime and change how your program runs?
Attributes let you attach metadata to methods, classes, tests, enumerations… pretty much anything. Then you can use reflection to read them at runtime and take some action. If you haven’t used them much before, or if it’s just been awhile, let’s look at a few practical examples.
It’s common for a .NET app to include a config file of some sorts (app.config, web.config, etc). These are great because you can simply change values, without having to recompile the app.
They’re slightly less great because there’s several ways to read those values from your code, and some of them are a pain. By using the ConfigurationProperty attribute, and any of the various classes that extend the ConfigurationValidator attribute, you can actually set up a class to represent the config file at runtime.
All of the major testing frameworks make use of attributes, whether it’s NUnit, xUnit, or MSTest.
Here’s a ridiculous Employee class, thoroughly vetted by some NUnit tests. You can decorate any method you want with SetUp and TearDown attributes, which act like a constructor and destructor run before and after every test. Tests have a Test attribute, and you can even reuse the same test for multiple values with the TestCase attribute. NUnit makes use of all these attributes to know what, when, and how to run things.
Another practical use for attributes is warning users of your library that you plan on deprecating some piece of code. Even Microsoft, known for its unprecedented backwards-compatibility in the .NET Framework, marks their code as obsolete from time to time.
usingSystem;namespaceAttributesExamples{publicclassPlannedCodeObsolescence{stringfirstName;stringlastName;intage; [Obsolete("SetFirstName and SetLastName are more reliable and replace this method.")]publicvoidSetName(stringname){varnames=name.Split();firstName=names[0];lastName=names[1];}publicvoidSetFirstName(stringfirst){firstName=first;}publicvoidSetLastName(stringlast){lastName=last;} [Obsolete("Function renamed to SetAge in v2.4 for consistency", true)]publicvoidAgeSet(intage){SetAge(age);}publicvoidSetAge(intage){this.age=age;}}}
When you mark something as obsolete, any attempts to use it are underlined in green as a warning. You might leave things like this for several releases, and when you’re ready you set the error parameter to true, which tells the compiler to treat usage as an error instead of a warning.
Instead, create an enumeration and use the Flags attribute so the system knows to treat it like a bit field. What is that, you ask? Great question.
Imagine you had a bunch of switches or flags, to signify “on” and “off” for a variety of settings. You could use 1 for “on” and 0 for “off”. So if you had three such flags, and the second was “off” while the first and third were “on”, you might represent them like this: 1 0 1
As it turns out, using bits to represent your flags is a perfect fit, but then how do you code that? Well, 101 in binary is the same as 22 + 21 + 20 = 7 in decimal. If you only use the power of 2 for each flag, you can combine values and know exactly which flags are “on”. The decimal value of 7 means the first and third flag are set, and nothing else. It’s completely unambiguous… and efficient.
Note the numbering for the enum values in the following example.. all powers of 2. By applying the Flags attribute, you can make use of other .NET code that allows you to quickly select multiple values at once, as well as quickly test which values are selected.
usingSystem;namespaceAttributesExamples{ [Flags]publicenumPreferredContactMethods{None=0,LandPhone=1,CellPhone=2,Email=4,SnailMail=8,Owl=16,FlooPowder=32,Text=64,Muggle=Email|Text|CellPhone,Wizard=Owl|FlooPowder}staticvoidDisplayPreferences(){varprefContactMethods=PreferredContactMethods.Email|PreferredContactMethods.FlooPowder;if(prefContactMethods==PreferredContactMethods.None)Console.WriteLine("User hates people. :(");// won't printif(prefContactMethods.HasFlag(PreferredContactMethods.LandPhone))Console.WriteLine("What's a landph one? :/");// also won't printif(prefContactMethods.HasFlag(PreferredContactMethods.CellPhone)||prefContactMethods.HasFlag(PreferredContactMethods.Email))Console.WriteLine("Aren't we modern? :p");// should printprefContactMethods|=PreferredContactMethods.Owl;if(prefContactMethods.HasFlag(PreferredContactMethods.Wizard))Console.WriteLine("You're a wizard Harry!! ~:›");// this too}}}
Okay, I tricked you. Number 5 is whatever you come up with!
For me, in creating a library called GhostSharp(a C# wrapper around an API that connects to Ghost blogs), I had a single class representing an object that could be POSTed to an API endpoint. The problem was that a PUT (update) to the same endpoint could only be a subset of those fields… trying to pass the same object failed. I could’ve created two objects - one for POST and one for PUT - but I really wanted to use a single object. The solution?
I created a simple attribute to dress up those fields that were acceptable for an update. I mean, really simple. There’s nothing else but a name, but that’s all I needed.
1
2
3
4
5
6
/// <summary>/// Represents a field that can be updated in a PUT request./// </summary>publicclassUpdatableFieldAttribute:Attribute{}
And here’s a portion of the class representing a post (as in a blog post, not the REST action POST 😅). The uuid and comment_id fields cannot be updated, but the title and feature_image can. Adding an attribute is only half the work though.
/// <summary>/// Request sent to Ghost/// </summary>publicclassPostRequest{/// <summary>/// Collection of posts./// </summary> [JsonProperty("posts")] [UpdatableField]publicList<Post>Posts{get;set;}}/// <summary>/// Represents a Post./// </summary>publicclassPost{/// <summary>/// ID (Update)/// </summary> [JsonProperty("id")]publicstringId{get;set;}/// <summary>/// UUID/// </summary> [JsonProperty("uuid")]publicstringUuid{get;set;}/// <summary>/// Title/// </summary> [JsonProperty("title")] [UpdatableField]publicstringTitle{get;set;}/// <summary>/// Post, formatted in Mobile Doc/// </summary> [JsonProperty("mobiledoc")] [UpdatableField]publicstringMobileDoc{get;set;}/// <summary>/// Comment ID/// </summary> [JsonProperty("comment_id")]publicstringCommentId{get;set;}/// <summary>/// Feature Image/// </summary> [JsonProperty("feature_image")] [UpdatableField]publicstringFeatureImage{get;set;}......}
I’m not going to get into the details of serializing a C# object to JSON, but with the updatable fields marked appropriately I had to write a short piece of code to tell RestSharp when a property should be included in the serialization. Using a little reflection, I could test to see whether the UpdatableField attribute was on a given property - if it is (!= null) then send it with the rest of the PUT.
Attributes are a handy way to add metadata to your code, which can be read back using reflection at runtime. In addition, they offer a bit of documentation too - I can look at my post class and quickly confirm which fields are included in an update.
If you’re interested in learning about a newer feature called generic attributes, introduced in C# 11, check out What are generic attributes in C# 11?. And if you want to learn more about a variety of C# features, check out my GitHub repo, where you’ll find links to plenty more blog posts and practical examples.