C# 7 feature request #1… extension attributes

Last week I learned that using static is going to be the syntax for importing static members (including extension methods) in C# 6. That fulfils a feature request I made in September 2005 (my fourth ever blog post, as it happens). With a feature request turnaround of 10 years, I figure I should get put everything I could ever want out there now… (Just kidding really – more seriously, I’m really pleased to see this change in C# 6, relative to both C# 5 and the earlier designs of C# 6.)

Last week at CodeMash, Dustin Campbell demonstrated one Roslyn DiagnosticAnalyzer for ArgumentNullException and friends, and I demonstrated one I’d written for the JetBrains InvokerParameterName attribute. The two diagnostics check largely the same thing: some parameters are “special” in that the argument for them should be a parameter in the current context, and should use the C# 6 nameof operator.

The most significant difference between them (beyond quality – it’s not hard to spot which is written by a C# team member…) is that my diagnostic spots these special parameters because they are decorated with a particular attribute, whereas Dustin’s has the relevant information hard-coded within the diagnostic. Mine can’t spot the ArgumentNullException constructor, and Dustin’s can’t spot the Preconditions.CheckNotNull method in Noda Time. It would be really nice if I could pretend that certain members of existing types had particular attributes applied to them. For example, wouldn’t it be nice if I could write something like:

using JetBrains.Annotations;

namespace System
{
    public extern partial class ArgumentNullException
    {
        public ArgumentNullException([InvokerParameterName] string paramName);
    }
}

The rules would be roughly like this:

  • A type declared with public extern partial modifiers would indicate that attributes should be applied to an existing type.
  • Each member listed would be checked for existence, and any attributes present in the declaration would be noted in the IL for the “extending” assembly.
  • A new call on Type, MethodInfo etc would be created to allow all attributes to be fetched for a member, whether “extended” or not. This would find all attributes contributed by all assemblies loaded in the current AppDomain (so you’d need to make sure that you did something to initialize any assembly containing extension attributes).
  • A similar new call would be available within Roslyn – we’d need to think carefully about which assemblies that examined, of course.

By making this an “opt-in” mechanism from the examiner perspective, it would be safe – anything dealing in security attributes would want to use the existing mechanism, for example.

This wouldn’t just be useful for diagnostic purposes, mind you. There are a number of situations where you might want to augment existing types with more metadata, whether those attributes are your own custom ones or existing ones in the framework. Want to apply an attribute-based serialization framework to a different third-party type? Sure, just use those extensions. Want to give system enums localization-oriented attributes for your own framework? No problem.

I’m not actually expecting this feature to go very far – it’s relatively niche, as well as possibly requiring CLR changes (rather than just framework and language changes). Still, having thought of it, I decided it would be odd to keep it to myself. And hey, it’s a good excuse to create the C# 7 category on my blog…

15 thoughts on “C# 7 feature request #1… extension attributes”

  1. ReSharper already resolves it through XML annotations. I don’t see why out-of-language approach such as XML/JSON doc wouldn’t be better here.

    Also there are two existing mechanisms in .NET for runtime extension attributes:
    1. ICustomTypeDescriptor
    2. ReflectionContext

    Those are both limited, but if there was an overridable default ReflectionContext it would solve most of the runtime use cases.

    Like

    1. Both of those might be implementations for custom attributes – but it would be nice to have a simple way of an assembly contributing them. As I said, I’m not really expecting this to go anywhere… but I do think that as there’s already a perfectly good way of specifying attributes, simply extending that mechanism is cleaner than XML/JSON documents. No different syntax to learn, no different language for the compiler to understand (I still want compile-time checking)…

      Like

  2. A while ago I submitted a request to FluentAssertions to validate that methods have an attribute (at the time it was for an MVC project, to validate that HTTP Post methods have an AntiForgeryToken attribute), which eventually became the BeDecoratedWith method… seems like a similar approach would be possible using a little bit of reflection.

    Like

  3. This sounds not dissimilar to the ASP.NET 5 (vNext) concept of Assembly Neutral Interfaces where a library can “duck type” declare an interface that “ought to be around here somewhere”, which the KLR/ASP.NET 5 runtime will patch up as it dynamically or statically compiles things. This is a conventions-based approach where they are sure that their own runtime will be used, but a general concept in the language to declare what ought to be there and pick things up could be the right answers for some problems.

    Like

  4. This would be useful in code generation scenarios as well; say you have a bunch of partial classes generated for EF in a ‘database first’ approach, but you want to enhance these classes with additional metadata via attributes on the properties… currently you may have to resort to Metadata classes without the ability to add attributes to existing members of a type…

    Like

  5. I’d love to see this, but see this syntax used for even more. For instance the ability to add interfaces to existing classes would be amazingly useful.

    Like

      1. public struct OriginalClassWithoutInterface. (e.g. int, float. The main reason I want this)

        Basically the current workaround is to make something like the Integer class in Java, and apply the operations there. It works, but is far from clean.

        Posted this on /r/programmingdiscussion and the biggest concern is where 2 different libraries both provide implementations for an interface.

        Like

Leave a comment