Better support of attributes in C#

Topics: C# Language Design
Oct 21, 2014 at 6:13 PM
Suppose I want to use a marker interface on a class
if(foo is IMarker) { //easy }
Why can't it be as simple as something like
if(foo has [Marker]) { }
As opposed to
typeof(Foo).IsDefined(typeof(MarkerAttribute), false);
Things get even more arcane if you want to test for an attribute on a member instead of the class itself.
Oct 22, 2014 at 4:28 AM
I like this. I don't know if the team would add a has keyword, but it should be easy enough to interpret the existing is keyword based on using the brackets.
if (foo is [Marker]) { }
Oct 22, 2014 at 10:58 AM
bondsbw wrote:
I like this. I don't know if the team would add a has keyword, but it should be easy enough to interpret the existing is keyword based on using the brackets.
if (foo is [Marker]) { }
That doesn't make any sense because foo is not a Marker.
Oct 22, 2014 at 11:04 AM
dotnetchris wrote:
Suppose I want to use a marker interface on a class
if(foo is IMarker) { //easy }
Why can't it be as simple as something like
if(foo has [Marker]) { }
As opposed to
typeof(Foo).IsDefined(typeof(MarkerAttribute), false);
Things get even more arcane if you want to test for an attribute on a member instead of the class itself.
With the new has keyword you're proposing, there's no need for the square brackets. And they don't even make sense because the type of the attribute is MarkerAttribute. Again, with the new has keyword you're proposing, the compiler can lookup for MarkerAttribute when you write Marker-

I like the pair is/as. Can you come up with an equivalent of as for the proposed has?
Oct 22, 2014 at 11:47 AM
I gots me one of these:

public static bool HasAttribute<T>(this object instance) where T : Attribute {
    if (instance == null) {
        return null;
    }
    Type type = instance.GetType();
    return type.IsDefined(typeof(T), false);
}
So I can use it like this:
if (foo.HasAttribute<MarkerAttribute>()) { }
Oct 22, 2014 at 12:59 PM
PauloMorgado wrote:
I like the pair is/as. Can you come up with an equivalent of as for the proposed has?
Am i following this correctly? We have "has" to determine is attributed with [Marked].

Now suppose I want to access the attribute to read properties on it? That with an interface we could as cast it?

Well, being cheesy there is aas "attribute-as". has/his would actually work
var marker = (foo his [Marker]) if(marker != null....
But i doubt they would want to pick a gender specific pronoun. Honestly i think "as" is sufficient here. foo as [Marker].

What hasn't been specifically talked about in detail is reading information off properties. Part of me is thinking the new nameof operator could be a great match up somehow.
Oct 22, 2014 at 1:28 PM
Edited Oct 22, 2014 at 1:28 PM
The problem with this is that the member/class can have an attribute applied multiple times and on ancestors as well, which one you get when using as?

Also how do you tell in the is case if you want to include inherited attributes or not?
Oct 23, 2014 at 4:54 PM
JanKucera wrote:
The problem with this is that the member/class can have an attribute applied multiple times and on ancestors as well, which one you get when using as?

Also how do you tell in the is case if you want to include inherited attributes or not?
One is the optimized path, the other can use the old APIs. I probably would choose the whole tree one, I can't really think of a situation of why the attribute I'm looking for is on the parent type and would not be sufficient.
Oct 23, 2014 at 5:26 PM
dotnetchris wrote:
One is the optimized path, the other can use the old APIs. I probably would choose the whole tree one, I can't really think of a situation of why the attribute I'm looking for is on the parent type and would not be sufficient.
The major semantic difference between a marker interface and a marker attribute is that if a class implements a marker interface, all subclasses will do so as well; by contrast, a class cam make promises via a marker interface which are binding on it alone, and not on any descendants. Such things may be useful for code which e.g. receives a type whose constructor it should call with a single parameter of type String which will be interpreted a certain way. The fact that a particular type's constructor interprets a passed string a certain way doesn't imply that any descendant will do so as well. If types with the proper kind of constructor are tagged with an attribute, code which wants to validate the attribute should not look for it in supertypes.