554

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?

9
  • 18
    You could do [Validates(typeof(string)] - I agree generics would be nicer... Commented Mar 1, 2010 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<T>: Attribute {} which could be used to create non-generic derived classes like this: class Concrete: Base<MyType> {}
    – Lucero
    Commented May 19, 2010 at 18:46
  • 97
    I crave for generic attributes and attributes accepting lambdas. Imagine things like [DependsOnProperty<Foo>(f => f.Bar)] or [ForeignKey<Foo>(f => f.IdBar)]... Commented Aug 10, 2011 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
    Commented Aug 22, 2012 at 18:42
  • 10
    If you don't mind a little IL, this looks promising. Commented Oct 9, 2012 at 18:39

8 Answers 8

383

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.

23
  • 148
    "except to avoid complexity in both the language and compiler"...and that from the people giving us co and contravariance...
    – flq
    Commented Jan 21, 2009 at 21:41
  • 283
    "use case which doesn't add much value"? That's a subjective opinion, it could provide me a lot of value!
    – Jon Kruger
    Commented Sep 21, 2009 at 19:00
  • 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. Commented Jun 25, 2011 at 12:38
  • 14
    @John: I think you vastly underestimate the cost of designing, specifying, implementing and testing a new language feature.
    – Jon Skeet
    Commented Aug 8, 2011 at 16:08
  • 15
    I just want to add in @Timwi's defence that this isn't quite the only place they are being discussed, and the 13K views on this question implies a healthy level of interest. Also: thanks for getting an authoritative answer, Jon. Commented Jan 29, 2013 at 11:51
93

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.

6
  • 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? Commented Nov 16, 2008 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... :) Commented Nov 16, 2008 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." Commented Nov 16, 2008 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. Commented Nov 16, 2008 at 20:44
  • 5
    This seems to be contradicted by another answer which states that IL will allow it. (stackoverflow.com/a/294259/3195477) Commented May 9, 2018 at 18:27
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
{
    ....
}
7
  • 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). Commented Nov 17, 2008 at 0: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. Commented Dec 31, 2009 at 18:12
  • This doesn't answer the question. Commented Jan 4, 2021 at 19:30
  • As GeekyMonkey stated - this is a workaround. It's better than waiting for C# 10 - so thanks for that
    – andrew.fox
    Commented Feb 8, 2022 at 10:54
  • @andrew.fox what in C# 10.0 fixes this?
    – IronSean
    Commented May 25, 2022 at 17:35
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
{

}
18

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

4
  • 2
    This is pretty great, but it does not seem to allow a parameter type Expression<Func<T, object>> (might be doing something wrong here) which means it's not possible to do something like: [Lookup<Child>(c => c.ParentId)] Commented Jan 1, 2022 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 Commented May 30, 2022 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 :( Commented May 30, 2022 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<Child>._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 Commented May 30, 2022 at 21:14
7

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.

3
  • 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. Commented Nov 16, 2008 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
    Commented Jan 13, 2010 at 21:54
  • 4
    you could check against the generic type definitions (i.e. typeof(Validates<>)) ...
    – Melvyn
    Commented Jul 24, 2013 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.

-2

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; }

Not the answer you're looking for? Browse other questions tagged or ask your own question.