-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Proposal] Allow generic attributes #953
Comments
3 and 4 are not pros, nor are they cons. 2 is marginal. 1 is the only justification that makes enough sense to consider it as a language feature. Do you have any use cases? |
To me 3 is important -- I don't see a reason why CS0698 exists in the first place, and removal of special cases should make language easier to learn and understand (especially since actual implementation can be done outside of the Roslyn team now). I agree on 2 and 4. Example attributes that could benefit from this (e.g. by defining a generic attribute subclass for old cases, and by defining the attribute as generic in new cases):
In fact ReSharper provides a special BaseTypeRequired annotation just for these cases -- allowing developer to specify the expectation. |
Ignore my previous comment (now deleted). The generic type arguments for a generic attribute type can be encoded in the MemberRef metadata table, by representing the "Class" field as a TypeSpec. |
Glad I'm not the only one. https://roslyn.codeplex.com/discussions/542178 2015-03-02 0:29 GMT+01:00 Sam Harwell notifications@github.com:
|
Problem is, the main advantage of this proposal is the support for generic constraints but the constraints can be easily circumvented by using the base attribute. You'd need to obsolete the TypeConverter's Type constructor to convince people to use the generic TypeConverter attribute and that's unlikely to happen. Besides, for some of these attributes (DesignerAttribute especially) it's more common to specify the type name rather than the type. There's also the question of performance, do you really want to instantiate tens or maybe even hundreds of such generic attribute type just to get those generic constraints? |
A long discussion on a closely related scenario with the opposite conclusion is in dotnet/corefx#271. |
How is that an issue that concerns the C# compiler? How is it an issue at all? Changing existing usage of attributes is not worthwhile, and the old attributes will continue to work, so why stop anyone?
Why? It literally has no benefit to anyone to obsolete the old attributes. It would be counter-productive.
How does that concern generic attributes?
Why is that the compiler's problem? |
Who says it the compiler's problem? This is a language feature, not some obscure compiler implementation detail that isn't part of the language specification. And as with all language features it's worth considering the pros and cons.
Use cases have been requested to support this language feature. Uses cases have been provided and I pointed out that some of these uses cases are not representative.
Yes, it would be counter productive to obsolete the old attributes and that's one of the reasons I already said that it won't happen. The side effect of not being able to do that is that the main selling point of this language feature (ability to use type constraints) is not very valuable for the presented use cases. For example, the consumer of TypeConverterAttribute still needs to deal with the fact that the specified type might not be a type converter after all.
Again, this is not about the C# compiler but about the C# language. A language would do well not to add features on the basis that "it is possible so why not?". A language feature should be useful, useful enough to pay for its cons. |
I don't really see it as a feature that would directly benefit those attributes (though even subclasses would make those somewhat nicer to use). The thing is -- there are a lot of APIs created for .NET all the time, including MS obsoleting some APIs, creating new APIs, etc. Based on the attributes we see now, it's not hard to see that those APIs are likely to use similar approaches -- and in those cases there will be no legacy parts to care about.
I am still not fully clear on how it works with new, open-sourced Roslyn. I think I read somewhere that the team had -100 points as a starting cost of any feature. However, what if the feature has no obvious cons and is implemented completely through contributors? Of course there would be still some work required to review it, but is it -100 anymore? |
The -100 points don't come primarily from implementation effort. It comes from increased complexity of the language. |
But doesn't removal of CS0698 decrease complexity? I can't speak for all developers, but for me language feels simpler when a feature such as generic classes works everywhere -- instead of breaking for edge cases (such as attributes). |
I suppose that depends on what exactly language complexity is. I could say that this feature adds an alternative way of doing things and that means that the developer has to make a choice. Which choice is better and why? Is the generic version really "nicer"? Nice is rather subjective, for example I can say that the non-generic version is nicer because it clearly shows what the code is doing while the generic version is hiding that. What bugs me the most about the generic attribute approach is that it encourages creation of generic types that don't really have to be generic. Let's look at how a generic type converter attribute would look: public sealed class TypeConverterAttribute<T> : TypeConverterAttribute {
public TypeConverterAttribute() : base(typeof(T)) {
}
} What exactly is generic about this type? Does it have a field of type T? No. Does it use T in a method signature? No. So, why generic? And it isn't true that there are no cons. There is certain overhead associated with generic instantiations. I believe that this overhead would be fairly small for typical attributes but it is certainly higher than 0 and it deserves at least to be mentioned. |
@mikedn Your points are all very valid. I don't see choice here as a bad thing, mostly because it is consistent with the same choice around type-accepting methods (
I think that logically it can easily be using T in type signature though: public sealed class MyConverterAttribute<T> /* : probably some interface */
where T : MyConverter, new()
{
public MyConverterAttribute() {
}
public T CreateConverter() {
return new T();
}
} It's just that |
I agree that removing otherwise arbitrary language restrictions would not trigger the -100 complexity penalty. Of course we still need to measure the bang-for-the-buck, by weighing the complexity of changing the language specification and implementation (of both C# and VB, and probably F# as well) versus the benefits of the change, and compare that to other things we could be doing. |
@mikedn https://github.com/mikedn public sealed class TypeConverterAttribute : TypeConverterAttribute { This is based on provided use case, but it is a bit contrieved 2015-03-04 2:43 GMT+01:00 Neal Gafter notifications@github.com:
|
That where the main question comes in -- considering that the change can be done by community -- how should that be weighted? At this point you probably know the approach better than me, but just in case it is useful: My first thought would be to put it on some low priority up for grabs list. Items there would never be implemented by Roslyn team because of other more important work. However, a full implementation can be accepted from the community. This isn't perfect as it still implies effort spent on reviewing the PR and defining acceptance criteria. However that would allow improvements that might otherwise never get in due to workload -- and people who want to improve things would be able to do actual coding and not just discussion. |
Yes, in general choice is good. Unfortunately not all developers make the best choices and sometimes it makes sense to limit choice.
You could do that but in most cases it won't be useful. The consumer of the attribute doesn't usually know the type at compile time so it would be impossible to use |
While the attribute usage is nice, retrieving them seems cumbersome. before:
after:
Even with a simple extension method to avoid repeating this logic over and over, you still need some base class or interface implemented (to cast your attribute to) to use it. This applies to every Off the top of my head, the only use case I see for generic attributes is when the generic type is used like an enum or as a filter:
Nothing that couldn't be achieved without a non-generic one and further filtering. That said, I do agree that not being able to use generics in attributes feels weird, and language consistency is important (same thing with nullable properties in attributes). |
How does |
Well, yes, that's why the generic TypeConverter I have shown above inherits from the non generic TypeConverter, so you can retrieve (and use) the generic attribute the same way you can retrieve the non generic one. Ashmind mentions "some interface" implementation in his
Probably it doesn't because the rules for this case haven't been written. |
The first step here would be a proposed update to the C# and VB language specifications. This isn't something we're planning, so we are not creating an issue for it beyond this one. But if someone does propose detailed specification changes, we would consider those specs and decide if a community-submitted implementation would be welcome. |
How is this done? As a pull request against the spec document? |
@ashmind The language spec documents are not open-source at the moment, so you'd have to write diffs (by hand) from some recent version of the published specs. |
Relevant discussion: http://stackoverflow.com/questions/294216/why-does-c-sharp-forbid-generic-attribute-types I would personally appreciate this feature. |
Reflection API support would also need to be added for this to work, currently |
will this be supported? void F<T>([Foo<T>] T t){} does it make sense at all? |
It's funny how attributes were made to look like classes but then suddenly have lots of unexpected (but motivated) limitations. Might've as well introduced an |
I don't know about regular attributes, but it does make sense to support it through #6671 void F<T>([[Foo<T>]] T t){} And of course, we should be able to use it in constraints (#2944):
|
I Implement(removed the validation code) this feature in the roslyn source
Unhandled Exception: System.NotSupportedException: Generic types are not valid. Source: https://github.com/AviAvni/roslyn/commit/9cf2b3a9b67bd4a554fdac71ccbcb47af2133169 I'm looking at coreclr and corefx |
For me I'd like to see at least support for defining abstract generic attribute classes. My use case for this would be to allow defining a generic validator attribute.
Currently this can be done through an interface instead but would be much cleaner to support defining an abstract generic attribute class directly. |
It would be better to define that enum as:
Thus the LSB it is a toggle, with optional holiday flag, and you only need to check |
That enum was just an example and doesn't have any bearing on the use case. |
@AviAvni any news? What's about coreclr/corefx? |
@Pzixel no sorry I'll again |
@Pzixel https://github.com/dotnet/coreclr/blob/565efeadebc4e57adda634396933e958ffd51bb0/src/vm/customattribute.cpp#L670 here the code where the exception happened need to remove the condition for generic type and look if it's OK or need to find way to instantiate generic type instance |
@Pzixel I successfully run the code tomorrow night I'll send PR for coreclr and roslyn |
@Pzixel Just open the PRs |
This is now tracked at dotnet/csharplang#124 |
Problem
For some attributes (e.g.
TypeConverterAttribute
) that are used to reference types, the current usage looks like this:This has two disadvantages:
TypeConverter
.Suggestion
I suggest that the generic attributes should be supported, including any generic constraints.
Usage example:
Pros
Cons
The set of attributes that benefit from this might not be that large.
Updates
Removed pro 4 as it wasn't a pro.
The text was updated successfully, but these errors were encountered: