Hands on with Attributes

| | Comments (1)

Reader Jef requested a hands-on tutorial with the new attribute feature via the Suggestion Box,
and I am only too happy to oblige. This topic has been blogged about once or twice, so I won't go into many details about what attributes are or why you'd use them. Instead, this will be a very quick and dirty couple of usage examples.

Let's start with an easy example: deprecating an API. Let's say you've got a very large framework (either internal, or external), and you want to start to steer people away from an API because it's horrible. Well, the best way to do this is to deprecate the API and tell people what they should be using instead. Thankfully, attributes make this blindingly simple! Let's make a quick example.

First, create a new class and name it TestClass. Then, add a method to TestClass:


Function HorribleAPI( i as Integer ) as String
return Str( i )
End Function

Now, in Window1, drag out a PushButton and in its action event, put the following code:

dim c as new TestClass
MsgBox c.HorribleAPI( 1000 )

As you can see, this is a pretty horrible API -- it used to hold a complex number to string algorithm, but then you found out about Str in the framework. Now you want to make sure people stop using HorribleAPI and just use Str instead -- so let's deprecate the call! REALbasic's compiler uses an attribute to deal with this for you, so you simply need to assign the attribute (and optionally assign a value to it) in order to deprecate an API.

Right-click on TestClass.HorribleAPI in the code browser, and select the "Attributes..." menu item. This brings up the Attributes Editor, where you can enter "Deprecated" as the name for your attribute, without the quotes. The Deprecated attribute takes an optional string value that will tell the user what the replacement API is, so enter "Str", with the quotes. So you should have:

Now, if you Analyze your project (via Project->Analyze Project), you should see your deprecation warning, tell you that you should be using Str instead. Ta da! You've just officially deprecated your API and used attributes to do it! Now, let's go one step further -- you just realized that TestClass is totally not necessary anymore since it's only API has been deprecated in favor of Str. So let's deprecate TestClass without any replacement. In that case, right-click on TestClass in the project item editor, and select "Attributes." Now we just want to enter "Deprecated" (sans quotes) and hit enter -- this is using the default deprecation form. If you analyze your project again, now you'll see two warnings:

Now, let's see how we can get attribute information at runtime. Let's change our test harness around slightly -- remove the code from the PushButton's action event. We're going to replace it with some introspection code that allows us to see the attributes on TestClass itself. Any Introspection.TypeInfo object has an array of attributes associated with it. You can access this array via the GetAttributes method call -- it returns back to you an array of Introspection.AttributeInfo objects. These objects have a name and a value associated with them. We're going to make a helper method that displays the name and optionally displays the value of an attribute that is passed into it. The method will look like this:


Private Sub DisplayAttribute(attrib as Introspection.AttributeInfo)
dim s as String = attrib.Name
if not attrib.Value.IsNull then
s = s + " = " + attrib.Value.StringValue
end if

MsgBox s
End Sub

We can call this method with the class' attributes as well as with the class method's attributes. Your push button's Action event should look like this:

Sub PushButton1.Action()
// Get the type information for the class
dim ti as Introspection.TypeInfo = GetTypeInfo( TestClass )

// Get the class' attributes
dim testClassAttributes() as Introspection.AttributeInfo = ti.GetAttributes

// Display the only attribute we expect to find
DisplayAttribute( testClassAttributes( 0 ) )

// Now get the only method on the class
dim mi() as Introspection.MethodInfo = ti.GetMethods

// Get the method's attributes
dim testClassMethodAttributes() as Introspection.AttributeInfo = mi( 0 ).GetAttributes

// Display the only attribute we expect to find
DisplayAttribute( testClassMethodAttributes( 0 ) )
End Sub

I left off any semblance of error checking -- that's up to the reader to implement. But this should give you a pretty good indication of how to retrieve attribute information and work with it. You'll notice that the DisplayAttribute method checks to see if the attribute's value .IsNull. If IsNull returns true, that means no value was provided by the user when specifying the attribute. Aside from that, the rest of the code should be pretty straight-forward. If you run the example, you'll see "Deprecated" displayed first, and then "Deprecated = Str" displayed second, which is what we'd expect to see.

I know this is a pretty lame hands-on demonstration, but it should be sufficient for expository purposes. If you have any questions, please ask them here! Also, if you'd like to skip all the typing yourself, you can download the example project here.

1 Comments

Thank you Aaron for this useful information. Since I had already seen that my suggestion was removed from the Suggestion Box I was not that surprised to see this post pop up on my RSS reader ;-)

Leave a comment