This project is read-only.

Extension properties

Topics: C# Language Design
Feb 2, 2015 at 4:08 PM
Edited Feb 3, 2015 at 8:52 AM
Extension methods allow us to add methods to existing types without creating a new derived type. They are very useful for adding functionality to already existing classes. In our STSdb project we use them a lot for adding general functionality to existing .NET collections and classes (SortedSet<T>, List<T>, IEnumerable<T> etc.)

It would be very valuable if C# also allows extension properties.

For example instead of:
    public static class DecimalExtensions
    {
        public static int GetDigits(this Decimal d)
        {
            int[] bits = Decimal.GetBits(d);
            int flags = bits[3];
            int digits = (flags & ~Int32.MinValue) >> 16;

            return digits;
        }
    }
and:
    decimal v = 3.14159m;

    int digits = v.GetDigits();
we could have:
    public static class DecimalExtensions
    {
        public static int Digits(this Decimal value)
        {
            get
            { 
                int[] bits = Decimal.GetBits(d);
                int flags = bits[3];
                int digits = (flags & ~Int32.MinValue) >> 16;

                return digits;
            }
        }
    }
and:
    decimal v = 3.14159m;

    int digits = v.Digits;
These kind of properties would be a natural extension of the extension methods concept.
Feb 2, 2015 at 7:17 PM
I think it is helpful to be able to have a set of methods that can act upon something of a given type but aren't defined within the type itself. My personal preference would have been to use a different syntax for accessing such methods than for accessing members [perhaps something like someEnumerable..Count() as a syntax to invoke the extension method). On the other hand, in most cases, even though it may not be visually obvious where the method being executed comes from, its invocation will still be a function call, and the return value will be a temporary. If an extension method Foo is defined that can operate upon type Bar, and at some later date a public method Foo is added to type Bar, then as long as the behavior of the public method is similar to that of the extension method, existing code will continue to work fine.

Unfortunately, the usage cases for extension properties coincide with the usages that would be likely to cause problems if a later version of a type defined a member whose name matched the extension. As a simple example, suppose that someone things Forble should have included a property Tag of type Object, and defines an extension property Tag which uses a static ConditionalWeakTable<Fnorble, Object> to associate Fnorbles with tags. Meanwhile, the author of Fnorble realizes that the lack of a Tag property is a problem and decides to add a Tag field, or a Tag property which uses a backing field in conventional fashion. If that were to occur, each Fnorble would effective have two Tag properties--one which was used by code that was compiled when the compiler knew of the Tag within the class, and one of which would be used by code which was compiled when it didn't. Nasty.

The problem is slightly mitigated by using method-call syntax rather than property syntax for extensions. If the extension has to use property-call syntax, it would likely use members named something like GetTag and SetTag. If the author of Fnorble decides to add tags, however, they would more likely be added using a Tags property. Thus, code which uses GetTag and SetTag would more clearly be expecting to see the extension, while code which used the property would be expecting to use the actual member. That wouldn't solve the problem of there existing two separate tags, but it would at least make it possible to distinguish which of them any particular piece of code was intending to access.
Feb 3, 2015 at 1:47 PM
The current usage of extension methods (with single dot notation) is brilliant. Personally, I would not prefer different notation access for distinguishing extension methods from instance methods. The equal notation for both method types actually shows how similar they are. Both instance and extension methods receive "this" as an argument - in the first case it is hidden from us, in the second it is specified by us. Thus, I think the current single dot notation is more than perfect.

As for the concerns about the ambiguity - they are reasonable. But ambiguity already exists in the current extension methods concept. If we have an extension method for a type and later this type is extended with similar method (with a matching signature), the compiler will invoke the instance method instead of our extension method. But, I think, this behaviour cannot be an argument for not adding extension properties - it could be an argument for removing the extension methods. Yet, this topic is for adding extension properties, not for removing extension methods.

Actually both of the issues - the current one with the replaced extension method invocation, and probably the future one with the replaced extension property, could be easily solved with a simple compiler warning. (For example: the warning when we hide an inherited member.) Thus, when we see such a warning we can decide how to proceed - whether to use the new instance method or keep the extension. If the instance method does not satisfy our needs, we can always rename our extension method or explicitly invoke it via the static class in which it is defined. Thus, an ambiguity warning is a good solution.

Extension properties can create problems (as your example shows). But these properties are natural extension of the extension methods... Extension methods already exist (with the current concept) and it is natural for extension properties also to exist. (Furthermore the properties are methods themselves.)

We could look at the extension properties not as an opportunity for adding additional members to a type, but as a possibility to access existing members in a different way. They can provide an additional view of existing members, not adding new ones. As the first example shows - the number of digits is already encoded in the decimal value, we just provide access to it. This access looks exactly like a property...
Feb 4, 2015 at 12:39 AM
Ideally, when name collisions occur between extension methods and conventional ones, the compiler would be silent if both version of the method are interchangeable, and squawk if they are fundamentally different. Indeed, if a compiler could somehow magically know when an extension method was written, that it was going to conflict with a future class member, it would be helpful if the compiler could squawk when the extension method was written, rather than waiting until the extension method is deployed and the class is written. Such advance warning would of course generally not be possible, but it would be helpful in cases where it were.

I don't see the distinction between read-only properties and methods as being overly important; indeed, VB.NET gets by just fine allowing the same syntax for the invocation of either. The primary value of properties comes in the ability to use them on the left-hand side of an assignment operator, and I would suggest that such properties would have a much higher likelihood of conflict with future class members than would most other kinds of extension methods. Perhaps there are significant usage cases for read/write extension properties which wouldn't bear much risk of conflict; can you offer some?

Personally, I wish C# and VB.NET had provided a "local type alias" feature, where a definition for a type alias could include any extension members that should be applied to that alias. Thus, if one said e.g. using GraphicsWithGoodies=System.Drawing.Graphics + GrafGoodies, then then variables and expressions of type GraphicsWithGoodies would be interchangeable with those of type System.Drawing.Graphics [as would be the case without the +GrafGoodies], but they would also have any extension methods in local class GrafGoodies available. Use of type GraphicsWithGoodies would indicate an intention to have code use extension members even if future class members use the same names; use of type Graphics would indicate an intention not to use the GrafGoodies extensions. Given a variable of type GraphicsWithGoodies gr, someone who saw calls to gr.DrawParallelogram and gr.DrawLine would know to look in GrafGoodies and then in Graphics. By contrast, someone seeing a call gr.DrawParallelogram would have no clear way of knowing where such an extension method might be found.

If a means were added to limit the scope of extension members, then I would think extension properties would be a fine idea. Unfortunately, the lack of any sort of scoping for extension member means that it's unclear when code wants to use extension members, and when it wants to use real members, and I think that problem is worse for read-write properties than for methods. Read-only properties aren't particularly problematic, but I also don't see them as having much advantage over methods.