553

This causes a compile-time exception:

public sealed class ValidatesAttribute<T> : Attribute
{

}

[Validates<string>]
public static class StringValidation
{

}

I realize C# does not support generic attributes. However, after much Googling, I can't seem to find the reason.

Does anyone know why generic types cannot derive from Attribute? Any theories?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Bryan Watts
  • 44,911
  • 16
  • 83
  • 88
  • 18
    You could do [Validates(typeof(string)] - I agree generics would be nicer... – ConsultUtah Mar 01 '10 at 19:43
  • 22
    Even though this is a very late addition to this question, it's sad that not only attributes themselves but also abstract attribute classes (which obviously cannot be instantiated as attributes anyways) aren't allwed, like this: `abstract class Base: Attribute {}` which could be used to create non-generic derived classes like this: `class Concrete: Base {}` – Lucero May 19 '10 at 18:46
  • 97
    I crave for generic attributes and attributes accepting lambdas. Imagine things like ``[DependsOnProperty(f => f.Bar)]`` or ``[ForeignKey(f => f.IdBar)]``... – Jacek Gorgoń Aug 10 '11 at 19:39
  • 3
    This would be extremely useful in a situation I just encountered; it would be nice to create a LinkedValueAttribute that accepted a generic type and enforced that type on the actual value specified. I could use it for enums to specify the "default" value of another enum that should be used if this enum value is chosen. Multiple of these attributes could be specified for different types, and I could get the value I need based on the type I need. I can set it up to use Type and Object but being strongly typed would be a huge plus. – KeithS Aug 22 '12 at 18:42
  • 10
    If you don't mind a little IL, [this looks promising](http://www.jasonbock.net/jb/Default.aspx?blog=entry.b0b97ed428874abb9dcc3fbb0f9b529d). – Jarrod Dixon Oct 09 '12 at 18:39
  • @JarrodDixon: Interesting approach. For the particular scenario I describe, I would really want to instance the generic attribute at the point of usage. I will keep the IL approach in mind, though. – Bryan Watts Oct 09 '12 at 19:51
  • I agree, point-of-usage is how I wanted this feature to work, too - maybe in a future version :) – Jarrod Dixon Oct 09 '12 at 20:17
  • 1
    What a coincidence. I'm here for a slightly different use case but also related to validation attributes. I want to use `ValidateIfAttribute`. – Shimmy Weitzhandler Jul 31 '17 at 00:12
  • 2
    This is being tracked for inclusion in a future version of C#: https://github.com/dotnet/csharplang/issues/124 – Ian Kemp Nov 07 '18 at 08:55

9 Answers9

384

Well, I can't answer why it's not available, but I can confirm that it's not a CLI issue. The CLI spec doesn't mention it (as far as I can see) and if you use IL directly you can create a generic attribute. The part of the C# 3 spec that bans it - section 10.1.4 "Class base specification" doesn't give any justification.

The annotated ECMA C# 2 spec doesn't give any helpful information either, although it does provide an example of what's not allowed.

My copy of the annotated C# 3 spec should arrive tomorrow... I'll see if that gives any more information. Anyway, it's definitely a language decision rather than a runtime one.

EDIT: Answer from Eric Lippert (paraphrased): no particular reason, except to avoid complexity in both the language and compiler for a use case which doesn't add much value.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 3
    I'm glad to hear generic attributes are legal IL. I would very much appreciate if you could ask the team! I had the chance to ask Anders at PDC and totally forgot :-) – Bryan Watts Nov 16 '08 at 19:40
  • 2
    I've asked... we'll see if they reply. – Jon Skeet Nov 16 '08 at 20:04
  • 148
    "except to avoid complexity in both the language and compiler"...and that from the people giving us co and contravariance... – flq Jan 21 '09 at 21:41
  • 3
    Just wanted to ask the same question about attributes. Too sad generic attributes aren't available. Even worse that there is no particular reason for that. :/ – Arnis Lapsa Jun 11 '09 at 11:58
  • 280
    "use case which doesn't add much value"? That's a subjective opinion, it could provide me a lot of value! – Jon Kruger Sep 21 '09 at 19:00
  • 3
    It would value for me also as I'm finding out there's no way around this limitation. – Chris Marisic Dec 31 '09 at 18:10
  • 38
    The thing that bothers me the most about not having this feature, is not being able to do stuff like [PropertyReference(x => x.SomeProperty)]. Instead, you need magic strings and typeof(), which I think kind of sucks. – Asbjørn Ulsberg Jun 25 '11 at 12:38
  • 6
    @asbjornu: Yes, although having an `infoof` operator would be nicer than having to use the lambda expression... – Jon Skeet Jun 25 '11 at 13:10
  • 3
    It's understandable to avoid unnecessary complexity in the language and compiler until you want the feature like I do right now. :) At this point I'd argue the language already has generics and Attributes so why not just moosh them together - maybe a summer intern needs something to do at Microsoft. – John K Aug 08 '11 at 15:33
  • 14
    @John: I think you vastly underestimate the cost of designing, specifying, implementing and testing a new language feature. – Jon Skeet Aug 08 '11 at 16:08
  • 2
    @Jon, Indeed, it was a bit tongue-in-cheek. I neglected inclusion of the standard emoticon at the end. – John K Aug 08 '11 at 17:31
  • 15
    I just want to add in @Timwi's defence that this isn't quite the only place [they are being discussed](https://www.google.co.uk/search?q="generic+attributes"+c%23), and the 13K views on this question implies a healthy level of interest. Also: thanks for getting an authoritative answer, Jon. – Jordan Gray Jan 29 '13 at 11:51
  • 4
    @Jon: There are some situations where a language forbids a construct (e.g. using `System.Enum` as a generic constraint) which would--in the absence of rules prohibiting it--simply "work" by default. While the designers are free to do whatever they want, the philosophy of only investing effort where it yields benefit would suggest that designers shouldn't expend effort prohibiting such constructs unless doing so yields some benefit. What would be the effect of simply letting generic attribute types behave however they "naturally" would if C# didn't bother forbidding them? – supercat Mar 08 '14 at 21:51
  • 1
    An `infoof` operator would be very nice! I think it eliminates the need for generic attributes and solves other issues like the one described in this question: http://stackoverflow.com/q/18119857/358761 – ygormutti Aug 05 '14 at 00:17
  • 2
    @ygormutti: C# 6 will have `nameof`... not quite as powerful as `infoof`, but significantly simpler. – Jon Skeet Aug 05 '14 at 06:40
  • The real value in adding generic attributes is in design-time type constraints. – N-ate Oct 29 '14 at 17:07
  • @tacos: You can get the release candidate of VS2015 with C# 6 support already. – Jon Skeet Jun 21 '15 at 09:29
  • 1
    @JonSkeet I didn't read ahead far enough, I saw the video a few weeks ago but I was asking about the generic attributes. It would really lower the barrier of entry for some newer C# programmers I think who get used to the kind of syntax used building forms and helpers in view templates. I can remember, only a few years ago, I was so excited when I realized I could make my own attributes after reading your book - but then disappointed when I could not do `[Cascade(x => x.CountryCode)]` - something with select lists I think, I can't remember - but man that would be a cool feature. – tacos_tacos_tacos Jun 21 '15 at 09:37
  • My solution is to just use `object` as the type and cast where necessary; defeats the purpose of asking the question, but offers a subtly better alternative. Attributes are already limited in the types they can store so the drawbacks of a "generic" attribute are negligible, if you ask me. –  Nov 01 '16 at 21:53
  • Its 2020 and C# 8.0 and generic attributes are still not allowed; To me the use case right now would have been to make "about the same as" `[SomeAttribute]`, but with the ability to restrict WHICH types can go there, because by doing `[SomeAttribute(typeof(SomeType))]` the program is bound to only fail at runtime and this makes developing programs harder and more tedious. I can't find another way around to get a compile-time error instead of a runtime error or a "hard-to-debug lenient "ignore this attribute if not valid"" – Felype Jan 17 '20 at 14:09
  • 1
    @Felype it's on the C# 10.0 roadmap, see end of csharplang discussion [here](https://github.com/dotnet/csharplang/issues/124) – McGuireV10 Sep 30 '20 at 11:21
  • @tacos_tacos_tacos that is exactly the use case I'm looking at as well. With the preview flag turned on, it does not allow `Expression>` but works fine with `Expression>` interestingly enough. – Charles Chen Jan 01 '22 at 16:29
  • @CharlesChen Did you file a defect for `Expression>`? It's not clear to me from the Generic Attributes proposal why this would not be allowed? – John Zabroski Jul 26 '22 at 16:20
91

An attribute decorates a class at compile-time, but a generic class does not receive its final type information until runtime. Since the attribute can affect compilation, it has to be "complete" at compile time.

See this MSDN article for more information.

GalacticCowboy
  • 11,663
  • 2
  • 41
  • 66
  • 3
    The article restates that they are not possible, but without reason. I conceptually understand your answer. Do you know of any more official documentation on the issue? – Bryan Watts Nov 16 '08 at 19:21
  • 2
    The article does cover the fact that the IL still contains generic placeholders that are substituted with the actual type at runtime. The rest was inferred by me... :) – GalacticCowboy Nov 16 '08 at 19:46
  • 1
    For what it's worth, VB enforces the same constraint: "Classes that are generic or contained in a generic type cannot inherit from an attribute class." – GalacticCowboy Nov 16 '08 at 20:09
  • 1
    ECMA-334, section 14.16 says "Constant expressions are required in the contexts listed below and this is indicated in the grammar by using constant-expression. In these contexts, a compile-time error occurs if an expression cannot be fully evaluated at compile-time." Attributes are in the list. – GalacticCowboy Nov 16 '08 at 20:44
  • 5
    This seems to be contradicted by another answer which states that IL will allow it. (https://stackoverflow.com/a/294259/3195477) – StayOnTarget May 09 '18 at 18:27
  • @DaveInCaz It is. But both answers are 10 years old at this point. IL allows it, but C# doesn't, and the reason is basically "we didn't want to". – GalacticCowboy May 10 '18 at 19:53
22

I don't know why it's not allowed, but this is one possible workaround

[AttributeUsage(AttributeTargets.Class)]
public class ClassDescriptionAttribute : Attribute
{
    public ClassDescriptionAttribute(Type KeyDataType)
    {
        _KeyDataType = KeyDataType;
    }

    public Type KeyDataType
    {
        get { return _KeyDataType; }
    }
    private Type _KeyDataType;
}


[ClassDescriptionAttribute(typeof(string))]
class Program
{
    ....
}
GeekyMonkey
  • 12,478
  • 6
  • 33
  • 39
  • 3
    Unfortunately, you lose compile-time typing when consuming the attribute. Imagine the attribute creates something of the generic type. You can work around it, but it would be nice; it's one of those intuitive things you're surprised you can't do, like variance (currently). – Bryan Watts Nov 17 '08 at 00:20
  • 16
    Sadly trying to not do this is why I found this SO question. I guess I'll just have to stick with dealing with typeof. It really feels ilke a dirty keyword now after generics have existed for so long now. – Chris Marisic Dec 31 '09 at 18:12
  • This doesn't answer the question. – O. R. Mapper Jan 04 '21 at 19:30
  • As GeekyMonkey stated - this is a workaround. It's better than waiting for C# 10 - so thanks for that – andrew.fox Feb 08 '22 at 10:54
  • @andrew.fox what in C# 10.0 fixes this? – IronSean May 25 '22 at 17:35
  • @IronSean - they are introducing generics in attributes in C# 10. E.g. `[MyAttribute]` – andrew.fox May 26 '22 at 18:06
  • @andrew.fox I looked further into it and C#10 did not ship with this, but it's in the beta release for C# 11 currently. – IronSean May 27 '22 at 13:05
18

This is not truly generic and you still have to write specific attribute class per type, but you may be able to use a generic base interface to code a little defensively, write lesser code than otherwise required, get benefits of polymorphism etc.

//an interface which means it can't have its own implementation. 
//You might need to use extension methods on this interface for that.
public interface ValidatesAttribute<T>
{
    T Value { get; } //or whatever that is
    bool IsValid { get; } //etc
}

public class ValidatesStringAttribute : Attribute, ValidatesAttribute<string>
{
    //...
}
public class ValidatesIntAttribute : Attribute, ValidatesAttribute<int>
{
    //...
}

[ValidatesString]
public static class StringValidation
{

}
[ValidatesInt]
public static class IntValidation
{

}
nawfal
  • 70,104
  • 56
  • 326
  • 368
13

Generic Attributes are available since C# 11. Now, this is possible:

[GenericAttribute<int>()]
public int Method();

However, this is not possible yet:

[GenericAttribute<T>()]
public int Method<T>(T param);

T is not known at compile time.

Also,

The type arguments must satisfy the same restrictions as the typeof operator. Types that require metadata annotations aren't allowed. For example, the following types aren't allowed as the type parameter:

  • dynamic
  • string? (or any nullable reference type)
  • (int X, int Y) (or any other tuple types using C# tuple syntax).

These types aren't directly represented in metadata. They include annotations that describe the type. In all cases, you can use the underlying type instead:

  • object for dynamic.
  • string instead of string?.
  • ValueTuple<int, int> instead of (int X, int Y).

Source: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#generic-attributes

Hossein Ebrahimi
  • 632
  • 10
  • 20
  • 2
    This is pretty great, but it does not seem to allow a parameter type `Expression>` (might be doing something wrong here) which means it's not possible to do something like: `[Lookup(c => c.ParentId)]` – Charles Chen Jan 01 '22 at 16:21
  • 14 years passed, and still not fully there yet... I think have to finally change the main programming language to something more... ***actively listening user voice*** eh – quetzalcoatl May 30 '22 at 21:01
  • @CharlesChen I imagine that embedding lambdas may be a bit more involved, since it's pretty complex compiler part with all closures etc (of course not in this context).. If only we've had operators like `methodof` or if only `typeof` could contextually return relevant Type/MethodInfo/FieldInfo/etc. Since a System.Type could be compile-constant enough to be decided with `typeof`, or embedded as an attribute's parameter, why Method/Property/FieldInfo's can't? sad :( – quetzalcoatl May 30 '22 at 21:10
  • It's a pity we can't `MethodInfo = typeof(string.ToString());` and be simplify what we now do with reflection for the most of the time.... Then, simple transformation like move param expression to `class Foo<>{ internal static Expr<...> _field = ...}` and then `[Lookup(typeof(Foo._field))]` and do the value-reading from the `_field` in Lookup's ctor.. sounds dead simple comparing to what happens with foreach/using/yield/await/etc.. Of course I imagine the compiler has no need for such `typeof` since it just could bake it in like Type.. if its serializable like Type.. but probably isn't – quetzalcoatl May 30 '22 at 21:14
8

This is a very good question. In my experience with attributes, I think the constraint is in place because when reflecting on an attribute it would create a condition in which you would have to check for all possible type permutations: typeof(Validates<string>), typeof(Validates<SomeCustomType>), etc...

In my opinion, if a custom validation is required depending on the type, an attribute may not be the best approach.

Perhaps a validation class that takes in a SomeCustomValidationDelegate or an ISomeCustomValidator as a parameter would be a better approach.

ichiban
  • 6,162
  • 3
  • 27
  • 34
  • I agree with you. I have had this question for a long time, and am currently building a validation system. I used my current terminology to ask the question, but have no intention of implementing an approach based on this mechanism. – Bryan Watts Nov 16 '08 at 19:24
  • I stumbled across this while working on a design for the same goal: validation. I'm trying to do it in a way that is easy to analyze both automatically (i.e., you could generate a report describing validation in the application for confirmation) and by a human visualizing the code. If not attributes, I'm not sure what the best solution would be... I might still try the attribute design, but manually declare type-specific attributes. It's a bit more work, but the aim is for the reliability of knowing the validation rules (and being able to report on them for confirmation). – bambams Jan 13 '10 at 21:54
  • 4
    you could check against the generic type definitions (i.e. typeof(Validates<>)) ... – Melvyn Jul 24 '13 at 20:05
5

This is not currently a C# language feature, however there is much discussion on the official C# language repo.

From some meeting notes:

Even though this would work in principle, there are bugs in most versions of the runtime so that it wouldn't work correctly (it was never exercised).

We need a mechanism to understand which target runtime it works on. We need that for many things, and are currently looking at that. Until then, we can't take it.

Candidate for a major C# version, if we can make a sufficient number of runtime versions deal with it.

Owen Pauling
  • 11,349
  • 20
  • 53
  • 64
3

Generic attributes are supported since .NET 7 and C# 11 (in preview in .NET 6 and C# 10).

-1

My workaround is something like this:

public class DistinctType1IdValidation : ValidationAttribute
{
    private readonly DistinctValidator<Type1> validator;

    public DistinctIdValidation()
    {
        validator = new DistinctValidator<Type1>(x=>x.Id);
    }

    public override bool IsValid(object value)
    {
        return validator.IsValid(value);
    }
}

public class DistinctType2NameValidation : ValidationAttribute
{
    private readonly DistinctValidator<Type2> validator;

    public DistinctType2NameValidation()
    {
        validator = new DistinctValidator<Type2>(x=>x.Name);
    }

    public override bool IsValid(object value)
    {
        return validator.IsValid(value);
    }
}

...
[DataMember, DistinctType1IdValidation ]
public Type1[] Items { get; set; }

[DataMember, DistinctType2NameValidation ]
public Type2[] Items { get; set; }
razon
  • 3,882
  • 2
  • 33
  • 46