How can I add visible information to C# classes using attributes?

816 Views Asked by At

I am trying to add information to a C# interface, and I am trying to add this information using attributes.

Say for example, that I have int parameters everywhere, and some of these int parameters are "row IDs" and some are "column IDs". I'd like to be able to do this:

namespace RowIdTest1
{
    using System;

    public class Class1
    {
        public int Get([RowId] int r, [ColumnId] int c) { return 3; }
        public void Set([RowId] int r, [ColumnId] int c, int x) { return; }
    }

    [AttributeUsageAttribute(AttributeTargets.All)]
    public class RowIdAttribute : System.Attribute { }

    [AttributeUsageAttribute(AttributeTargets.All)]
    public class ColumnIdAttribute : System.Attribute { }
}

so that when another programmer uses my DLL and browses to Class1 in Visual Studio, he or she would see this in the "Class1 (from metadata)" window:

namespace RowIdTest1
{
    public class Class1
    {
        public Class1();

        public int Get([RowId] int r, [ColumnID] int c);
        public void Set([RowId] int r, [ColumnID] int c, int x);
    }
}

When I try it, the attributes don't appear at all.

I know you can do this sort of thing with ///-comments.

I know that you can use the System.ComponentModel.DescriptionAttribute class on anything (although descriptions on methods will appear but descriptions on parameters will not appear).

Am I doing something totally weird with C# attributes? Inappropriate? Insane?

5

There are 5 best solutions below

9
On BEST ANSWER

Am I doing something totally weird with C# attributes?

Yes.

You are trying to use an attribute to express a business domain fact about the parameter. That's not what attributes are for. Attributes are for expressing facts about the programming language mechanism. That is, when you say:

[Obsolete]
class BlackAndWhiteTelevision {}

That is saying that the class itself is obsolete and that you should no longer use it because the class has been replaced with a different class. Do not use the obsolete attribute to state that the physical world thing that the class represents can no longer be manufactured and sold at a profit.

That a particular parameter represents a row identifier is a fact about the business of your method, not about the mechanism of the method. You should put attributes on method parameters that express things like "this parameter has a default value" or "this parameter should be marshalled by value when called via COM interop" or "this parameter is going to be written to before it is read from" -- those are facts about the parameter as a variable in a programming language, rather than facts about how the parameter models some concept in your business process.

For more thoughts on how to use attributes to express facts about mechanisms instead of business domains, see my article on the subject and the comments to it:

http://blogs.msdn.com/b/ericlippert/archive/2009/02/02/properties-vs-attributes.aspx

0
On

No, you can't. Why not just name the arguments

public int Get(int rowId, int columnId)

?

0
On

I have to say I don't understand this very well. Is it common that users of your library (I assume) have to decompile your classes to look at the metadata? Why not just write some XML documentation for the interface and ship it with your library instead?

2
On

Am I doing something totally weird with C# attributes? Inappropriate? Insane?

In general, the reason to use attributes is because you want to have certain information about a class or its members available to your program via reflection at runtime. This isn't in keeping with that idea, so I'd say it's an inappropriate use of attributes.

I think a more preferred way to give the programmer a subtle hint that you want the parameter to represent a rowId is to use the parameter name:

public int Get(int rowId, int columnId)

Couple this with /// comments, like you mention, and you have a pretty effective way to tell consumers of your class what your expectations are.

Finally, if you really want to prevent users from accidentally providing the arguments in the wrong order, you could leverage strong typing.

public struct RowId {
    private int _rowId;
    public int Value{get{return _rowId;}}
    public RowId(int rowId) {_rowId = rowId;}
}


public int Get(RowId rowId, ColumnId columnId)

This would make it nearly impossible for someone to accidentally mix up the inputs. (Note that I am not recommending this pattern: It introduces a lot of overhead for very little benefit.)

0
On

I don't know if it's "insane". If it isn't working as is, maybe, assign the attributes to nested items inside of some kind of data structure, and then maybe pass the data structure as a parameter to the function. Just one idea.