dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.

Home Page:https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Language Feature: Extension Everything

gafter opened this issue · comments

There are existing proposals/requests #3357, #4945, #5165, #5624, #6136, but this is a slightly different take. One difference is that qualification with this would be required inside, at least for access to extension members.

Extension Everything for C#

This proposal gives a way to define new kinds of extension members. Today it is possible to define methods that act as if they are instance methods of the extended type. This proposal expands that capability, supporting both static and instance members, and supporting (or at least discussing) all of the kinds of members you might want to declare.

Here is an example that summarizes the new proposed syntax forms:

// "extension" is a new (contextual keyword) modifier, permitted before the keyword "class".
// It can be combined with "partial". It produces a static class that one would not normally use
// directly, however there will be syntax forms for directly using all of the named members
// (but not the operators) as members of this class.
public extension class ListListExtensions<T>
  : List<List<T>> // the "extends" clause names the extended type. May be an interface
  // , Interface  // we do not permit adding interfaces in the base clause
{
   // static variables are no problem, and hosted in the container ListListExtensions<T>
   private static int _flattenCount = 0;

   // static methods are simply hosted in the container, ListListExtensions<T> in this case
   public static int GetFlattenCount()
   {
     return _flattenCount;
   }

   // static properties are similarly hosted in the container.
   // They translate into a static "indexed" property, indexed with a "this" (extension) parameter.
   public static int FlattenCount
   {
       get
       {
         return _flattenCount; // extension static variable access
       }
   }

   // instance methods are compiled using the same pattern as existing instance extensions.
   // The additional hidden parameter is named @this from the SemanticModel's point of view.
   public List<T> Flatten()
   {
     // Within instance members, "this" is of type List<List<T>>
     _flattenCount++;
     ...
   }

   // Extension indexers permitted, both static and instance.
   public List<List<T>> this[int[] indices] => ...;

   // We do not support instance extension fields, but we could support them in the future
   // if the type being extended is a reference type, using ConditionalWeakTable.
   // public int InstanceMember;

   // We do not support constructors, but we could support them in the future
   // by creating an ordinary method with a special name. Some design work would
   // be required to ensure the following syntax is possible, or to design an alternative.
   // public List<List<T>>(int defaultSize) : this(...) { ... }

   // Operators are permitted. Operators are required to have a parameter (or return type,
   // for implicit conversions) of the extended type.
   // All of the lookup rules for operators will need to be amended to look in extension types
   // that are in scope.
   public static implicit operator List<T>(List<List<T>> self)
   {
     return self.Flatten();
   }

   public static implicit List<List<T>> operator +(List<List<T>> left, List<List<T>> right) => left.Concat(right);
}

Limitations:

  • Instance fields not permitted (at first)
  • Constructors not permitted (at first)
  • Events not permitted (at first)
  • The base clause cannot add interfaces
  • No members may be virtual, override, abstract, sealed, etc.
  • Private helper members (e.g. _flattenCount above) work the same as extension members, even though not accessible outside.
  • Operators will require some language constraints on built-in types, interfaces, etc. For example, the extended type must be either the source or target of a conversion operator.
  • No instance auto-properties (until we support instance fields).

Substantial LDM design work will be required

  • What is the syntax?
  • What is allowed and not allowed? Extending int[]?
  • We will need to describe how we go from a member access e.M to the set of extensions that might be designated by the access.
  • Every construct that can use one of these will have to have its spec amended
    • Member access, method overload resolution
    • Indexing
    • Every kind of operator (e.g. what are the constraints at the declaration, where do we look on the use)
    • Later, constructors
  • For conversions, what limitations do we need to not run into trouble?

Why we still require to name the extension class while it can be self explanatory like:

public extension int { }
// instead of
public extension class IntegerExtensions : int { } 

Using class-base to specify the target type can get weird in case of generic, static or array types:

public extension class IntegerArrayExtensions : int[] { }
// for defining static extension methods ..
public extension class ConsoleExtensions : Console { } 
// would this be allowed?
public extension class GenericExtensions<T> : T /* where T : ... */ { }

Alternatively,

public extension int[] { }
public extension Console { }
public extension<T> T /* where T : ... */  { }
public extension<T> List<List<T>> { }

This would reserve class-base for when we have virtual extension methods (#258) to add interface implementations to existing types (#8127).

I apologize if I misunderstood the proposal, but I don't think the way it treats this makes much sense.


return this._flattenCount;

So, static extension members are accessed from static extension members using this., which looks like instance member access? I think that's very confusing.


The additional hidden parameter is named @this from the SemanticModel's point of view.

So, to keep a static cache of the last instance some member was called on, I would have to write the following?

this._cache = @this;

Again, quite confusing.

@alrz I think the name can be useful. You can use it to explicitly call the extension method as a normal method (e.g. Enumerable.ToList(query)). And it also allows you to select only some extension types from a namespace (e.g. using static System.Linq.Enumerable;).

@svick

I apologize if I misunderstood the proposal, but I don't think the way it treats this makes much sense.

It was messed up. Static members are accessed by qualifying with the extended type. I've fixed up the OP.

So, to keep a static cache of the last instance some member was called on, I would have to write the following?

this._cache = @this;

Again, quite confusing.

The @ is to show that it is an ordinary identifier when viewed through the symbol table (compiler API), and that you can access it that way if you want. You can still refer to it using the this keyword.

@gafter Ok, now it looks good to me.

@svick Currently you can not use using static for extension methods. Also they can't be defined in generic classes. As you can see in the original post ListListExtensions<T> is a generic class (I'm not saying that this can't be done in the new syntax, though).

You can use it to explicitly call the extension method as a normal method

If that's your concern you'd better use the existing extension syntax (that'd be the benefit for the old syntax to be not totally deprecated). But I don't expect to be able to use extension methods as if they are static methods when I explicitly defined them in an extension declaration as non-static members!

And it also allows you to select only some extension types from a namespace

I didn't suggest that they be in the scope out of nowhere, just like what we have with extension methods, the containing namespace must be imported using using.

I think naming classes like extension class WhateverExtensions : Whatever is just too repetitive.

@alrz

Currently you can not use using static for extension methods.

This compiles fine for me:

using static System.Linq.Enumerable;

class Program
{
    static void Main()
    {
        new int[0].ToList();
    }
}

@svick Oh right. But this doesn't ToList(new int[0]); assuming using static System.Linq.Enumerable;.

While I do agree that naming an extension class is useful, I think that (formally) deriving this class from the extended class makes little sense overall.

public class ClassToExtend
{
    public ClassToExtend (int arg) { ... }
}

public extension class ExtensionClass : ClassToExtend
{
    // For when constructors are supported.
    public ExtensionClass (int arg1, int arg2) { ... }
}

...

// Unlikely the desired syntax, but should make sense if
// ExtensionClass is a descendant of ClassToExtend.
ClassToExtend a = new ExtensionClass(1, 2);

// Likely the desired syntax.
ClassToExtend b = new ClassToExtend(1, 2);

// Totally not what we need.
ExtensionClass c = ...

In short, extension class is not a descendant of the extended class, so it shouldn't look like one.
The syntax could rather go like this:

public extension class ExtensionClass<T> for ClassToExtend<T> { ... }

@quinisext

I do agree that naming an extension class is useful,

Can you give some examples, how is that useful? I also note that if your extension class is generic, which it very may well be, you can't use using static as @svick mentioned. This is not a problem right now because extension methods are generic, not the enclosing class, so using static is fine. Even if you could "use extension methods as if they are static methods even though you explicitly defined them as non-static members" which makes little sense, type inference won't help you and you will need to explicitly specify type arguments. So for complete compatibility with existing extension classes, generated class must not have a type parameter list and perhaps with an optional name, because it's not always useful.

@alrz
Even if the name is optional, I believe the class keyword should be obligatory.

public extension class<T> for T[] { ... }

(I'm not insisting on for, of course, just not :).
It makes the code clearer to read (especially when extension is not highlighted by the editor) and more uniform, and most probably easier to parse. Without it, the extension would probably need to be promoted to a full-fledged keyword status. Naming makes the code more uniform too, though.
Actually i'd rather have as little new syntax/keywords as possible:

// Only a for-part is something new; and no new keywords, contextual or not.
public class ArrayExtension<T> for T[] { ... }

// Easier to get as a special case of the previous. Still no good looking.
public class for ExtendedClass { ... }
public class<T> for T[] { ... }

Names would allow to using static an extension type without using the rest of its namespace.
Names are useful to me personally, due to the way I use reflection (which I do extensively) (can be achieved through attributes though).
Names would allow to create two distinct extension classes in one namespace, which can be using static separately.
They're also future-proof, in case some day language gets parametrized usings.

using static SomeNamespace.ExtensionClass<System.String>;

Admittedly these all are pretty esoteric.

Still the syntax you proposed does seem appealing to me, I'm just afraid it would complicate parsing, and the word "extension" is too good a word to make it a keyword. Maybe something like

public extending<T> T[] { ... }

Interesting 👍

Which existing types in CoreFx or third parties could benefit from the feature?

commented
  • Spec seems interesting and reminiscent of some previous community suggestions, however, I personally think you don't get much value-add over existing extension methods unless you add the ability to have data associated with an instance (extension instance properties).
  • Agree with previous comments that re-using the extends operator, :, is a bad idea. It already has too many meanings, don't add another one.
  • @gafter You've previously told me in another one of these threads that using ConditionalWeakTable for associating data with an instance slows down the entire program, even where it's not being used. Is this not still the case? Why would extension instance properties be limited to value types?

@MgSam;

  • Spec seems interesting and reminiscent of some previous community suggestions, however, I personally think you don't get much value-add over existing extension methods unless you add the ability to have data associated with an instance (extension instance properties).

Isn't this supposed to be static extensions? Why is "add the ability to have data associated with an instance (extension instance properties)" important?

  • Agree with previous comments that re-using the extends operator, : , is a bad idea. It already has too many meanings, don't add another one.

That's when Java and Visual Basic start to look good for having extends and implements keywords. But for C#, I think it's too late to go that way. Another punctuation?

commented

@paulomorgado My point was this seems like a lot of design work for very little benefit over current state if adding state to the instance is not on the table. Having extension methods on static classes are the only truly new use cases in this spec, but even that is less useful than it once was with C# 6.0's using static feature. (You can just dump all the static classes you want into scope in your file)

Agree the ship has sailed long ago on extends and implements, but that doesn't mean we should perpetuate the mistake by reusing : yet again. Make it an English-language keyword word rather than punctuation.

@MgSam
I use extension methods extensively (sorry), so I miss such feature for quite some time (it is in Delphi/Free Pascal for a long time now).
It allows adding properties using standard syntax, and that is enough benefit for me on its own.

commented

@quinisext I use extension methods extensively too. The only difference this proposal makes for properties is that instead of having extension methods SetFoo and GetFoo, you now can make those a property. But they still can only address static data. That doesn't seem like it's worth the design cost to me.

On the other hand, if there is a realistic way to associate instance data, then it becomes a big win. WPF has the whole concept of "attached properties", which are absolutely nasty to declare, yet this proposal still doesn't fill the huge niche that those attempted to address. The C# design team tried to tackle extension everything once before and rejected it because the design was incompatible with this use case.

I consider "extension static members" including "extension operators" the most important benefits of this.

@MgSam
I'm actually more interested in readonly properties. But having properties you can always implement
your own mechanism of attaching them:

public class ExtensionClass for System.Windows.Controls.Button  // Whatever would be the syntax.
{

    // You would still need to deal with abandoned values, but there is probably no good way
    // to solve this problem in general case.
    private static Dictionary<MyClass, int> values;

    public MyIntProperty 
    {
        get { return value[this]; }
        set
        {
            values[this] = value;
        }
    }

    // Some neat tricks.
    public double CanvasLeft
    {
        get { return Canvas.GetLeft(this); }
        set { Canvas.SetLeft(this, value); }
    }

    public RoutedEventHandler ClickHandler
    {
        get { return Click;   }
        set { Click += value; }
    }

    // Extension constructor.
    public ExtensionClass (out Button variable)
    {
        var result = new Button();
        variable = result;
        return result;
    }
}

// Now also this coud work.
var button = (Button)null;
var Panel = new System.Windows.Controls.Canvas
{
    Children =
    {
        new Button (ref button)
        {
            CanvasLeft    = 20,
            MyIntProperty = 42,
            ClickHandler  = (s, e) => { DoSomething(); }
        }
    }
};
button.Click += MyOtherHandler;

You can't do these things with Set/GetSomething().

Extension constructors may work well with immutable data. And extension operators would allow you to connect mathematical types that was initially unaware of each other.

If the concept of "extended" state is to be on the table I would like to see something come out of the CLR that would enable such functionality without incurring the penalties associated with ConditionalWeakTable. Otherwise my opinion is that C# shouldn't encourage such use and that if people want the "extended" state then they can simply follow those patterns manually.

Beyond that, extension static methods, properties and operators do seem like they'd be worth it. Extension constructors are weird but I can see their value. Maybe that could lead to asynchronous constructors #6788.

@quinisext That's a memory leak. Correct implementation would need something like WeakReference Dictionary. I'd rather see language support for extension fields than implementing this (I would probably get it wrong).
EDIT: I have just found out that there is something like WeakReference Dictionary in .NET already - System.Runtime.CompilerServices.ConditionalWeakTable class. It's only downside is restricting values to reference types.

@zippec

That's a memory leak.

That's exactly what I meant by

You would still need to deal with abandoned values.

As I said, I don't see a good general solution, that can be used as an implementation for extension fields/auto-properties. But that was not the point of the example. The point was to show that extension classes are worth implementing even without that particular feature.
As to the feature, it would be nice to have it somehow, but is there a really good way to make it? If you do it manually, you at least see what's going on and understand the costs and limitations.

Another idea for the syntax - just use the type that we are extending, possibly with additional constraints.

public extension class List<T> {..}
public extension class List<T> where T: List<int> {..}
public extension class List<T> where T: class {..}
public extension class List<T> where T: int {..}

@VSadov

What would the name be for the generated static class if the developer doesn't explicitly provide one? Whatever it is would have to be predictable in order for a recompilation to not break existing consumers, and it would have to be resilient to collisions in case you want to extend two different classes named List, or with a different arity of generic parameter types.

@VSadov
Would it scale well?

public extension class int[] { ... } // Formally works but unnatural for c#.

Also, as HaloFour said, what is the "predictable" name for int[] extension class? It is just unobvious in general case.

The challenge here is to have a syntax very similar to a class definition, but yet we want to sneak in a type reference to a potentially more constrained instantiation of a type - like List< int >.

My suggestion may not even be better than what @gafter proposed. Just another idea how we can abuse preexisting syntax.

Having no name is ok. Not everything in the source ends up with formal names.
As implementation, compiler can generate something unspeakable and specify applicability of the extension container via attributes.

@VSadov

Unspeakable would be perfectly fine as long as the name is determinstic based on the extended type.

🍝

Extended Generated Name *
int[] 'System.Int32[]<>Extensions'
List<T> 'System.Collections.Generic.List1<>Extensions'` **
List<int> 'System.Collections.Generic.List1<System.Int32>Extensions'`

* Note that the namespace is embedded in the class name.
** Would the static type itself be generic, or would only the static extension methods be generic?

@HaloFour What if you extend the same type twice in the same namespace (most likely from different files)? E.g.:

public extension class List<T>
{
    public void ExtensionMethod1() {}
}

public extension class List<T>
{
    public void ExtensionMethod2() {}
}

The only solution I can think of would be to combine both extensions into the same class.

@gafter Yes, yes, yes! I love this proposal. It would go a long way to allow layered designs without sacrificing on discoverability. :) We struggled with it consistently in the evolution of .NET Core and this would let us restore source compatibility to some of the harder decisions that were made. For example, Delegate.CreateDelegate and Char.GetUnicodeCategory could be made to work again.

// We do not support instance extension fields, but we could support them in the future
// if the type being extended is a value type, using ConditionalWeakTable.

Should this read "... if the type being extended is not a value type, ..."?

@VSadov @HaloFour Maybe I'm missing something, but if the container is unspeakable than there will be no syntax for forcing a call to an extension over a first-class member defined on the target, or to disambiguate between competing extensions. It would also seem to leave any .NET language that has not caught up to the metadata conventions backing this with no way to invoke these members.

@nguerrera That's a good argument for requiring the developer to explicitly name the extension class.

@VSadov

The challenge here is to have a syntax very similar to a class definition

I think it doesn't make sense to do that because extension is essentially a different kind rather than just a modifier. For example, take Module in VB, it turns everything to a Shared member (and Shared itself is not permitted), it's not a modified Class it's a different kind. In contrast, we require to use static on members in a static class, so there is nothing magical about static modifier. On the other hand, extension does permit static modifier on members, it's a separate kind in any way.

Note that with your suggestion it's not possible to declare an extension class for a generic T.

Another thing about existing extension classes is that they are always non-generic classes so I'm thinking for maximum compatibility e.g. rewriting existing extension classes as extension declarations, it should generate a non-generic class, regardless of the extension itself being generic.

Would be nice if extension members took structs by ref, not by copy.

@omariom

#165 seeks to allow that for C# extension methods in general. As mentioned there this is already a supported feature in VB.NET.

Question here would be if #165 is adopted then would extension classes always expose the this argument as ref if the extended type is a value type?

Re: unspeakable name.
Yes. After thinking about it, it seems that sooner or later there will be a need to invoke an extension directly. That would require the container be referable by name, so my suggestion would not work.
I am convinced.

Rust's attaching methods to the data is kind of similar.

@HaloFour I think it should. And while this is new feature anyway we could have it better than we already have

And I could say that we most likely to use extension method on struct. Because normal class could be extended as a derived class but struct cannot. But to have extension method only pass by value stop it. And it is design flawed all along that it can't fix because backward compatible

So now we can fix it with new syntax then it is a must

Still I'm not agree with operator. Because we can't find reference of duplicate operator like we can find member

@gafter Would it not be possible to support getter-setter properties without supporting fields? There is still some debate about the impact of ConditionalWeakTable, but supporting getters/setters:

  1. Allows users to add convenience properties to classes they can't/won't extend
  2. Allows users who accept the costs of ConditionalWeakTable to explicitly implement "fields" using static members for bookkeeping and properties for access

@masaeedu If you can't have a field, where would you store the ConditionalWeakTable?

@gafter Static field, and I'd do the bookkeeping for associating instances with their data explicitly.

@masaeedu Yes, if we support static fields then you can use ConditionalWeakTable to simulate instance fields for reference types.

You would need to box simulated fields to maintain right semantincs. Fields are variables - can be passed byref and so on.

So it would actually need to be something like ConditionalWeakTable<TContainer, StrongBox<TField>>

It would not be very efficient, but yes, extension fields are possible in theory.

I think we should not making extension field automatically like that. We may want to implement it with our own

@gafter Why would you not support static fields? If you don't, the workaround would be to create a new static class to hold the fields which is a bit ugly. Especially for encapsulation.

@gafter Will this "we do not permit adding interfaces in the base clause" be allowed at some point? I assume it wouldn't, I know that it requires CLR support but besides this is there any reason not to allow it?

@eyalsk It can get out of hand,

// lib 1
interface A {}
interface B {}
// lib 2, references: lib 1
class C : A {}
// lib 3, references: lib 1 and 2
void F(A a) {}
void F(B a) {}
F(new C()); 

The last line compiles but if you reference another library which happen to have an extension for C that implements B, it suddenly breaks. In Rust, however, either the target type or the trait must be declared in the same crate. I suppose this can be enforced in C# as well. See #8127.

@alrz Thank you. :)

I apologize if this syntax has been suggested before and I somehow missed it. This suggestion requires no changes to the C# grammar, and works for pretty much any kind of member you want to add. In fact, this example below is valid C# 3.5.

I like the fact that this syntax is explicit, that these declarations look exactly like what they are: static methods of a static class to which the compiler is redirecting.

I'm not a big fan of unnamed "extension classes" in C#. This may work well for other languages but I don't think it's appropriate for a CLR language designed to interoperate with other languages. The first problem is how these extension classes will appear when consumed outside of C# or by previous versions of the language. Next, there is ambiguity between what is a member of the extension class, and what is a member of the class being extended. And finally, there are many features of ordinary classes that need to be restricted in an "extension class" context: protection levels, finalizers, virtual/override/new, fields, interface implementations, and probably more. The cognitive load here is quite high--it's another whole kind of type to learn.

So, there is no magic happening in this syntax; no automatic weak-reference dictionary for properties or events. You're free to do that on your own, or perhaps the framework could provide something a little more friendly than ConditionalWeakTable. I think this is the best way to handle the situation because many types expose their own property/event dictionaries (Control, DependencyObject, etc) and are best extended in this way.

Like extension methods today, it is possible to declare and consume these extension members using any .NET language (even those that do not support extension methods/members) and with any CLR version.

Here is the code:

// Property getters and setters
// Compiler restricts the number of arguments and return types.
// When accessed as a property, the "Get" and "Set" prefixes are omitted just like the
//   "Attribute" suffix is omitted from attribute classes.
// There is no compiler-generated storage mechanism and no "auto" extension properties.

[Extension(ExtensionType.GetProperty)]
public static string GetTrimmedText(this Control control)
{
    return control.Text.Trim();
}

[Extension(ExtensionType.SetProperty)]
public static void SetTrimmedText(this Control control, string value)
{
    control.Text = value.Trim();
}

// Event accessors
// Compiler restricts the number of arguments and the return type must be void.
// When accessed as an event, the "Add" and "Remove" prefixes are omitted.

[Extension(ExtensionType.AddEvent)]
public static void AddMouseTripleClick(this Control control, MouseEventHandler e)
{
    // ... etc
    control.Events.AddHandler("MouseTripleClick", e);
}

[Extension(ExtensionType.RemoveEvent)]
public static void RemoveMouseTripleClick(this Control control, MouseEventHandler e)
{
    // ... etc
    control.Events.RemoveHandler("MouseTripleClick", e);
}

// Static extension members are declared without a "this" argument, and by providing
// an additional parameter to the ExtensionAttribute to say which type will receive the member.
// This usage should work for all kinds of members (not just methods).

[Extension(typeof(Color))]
public static Color FromHwb(double hue, double whiteness, double blackness, double alpha)
{
    // convert HWB to RGB ...
    return Color.FromArgb(...);
}

// Extension constructor
// The return type determines the type for which the constructor is being declared.
// We will probably want some restrictions here for enum types, static classes, etc.
// The name of this member is not used, it is called like `new ReturnType(...)`.

[Extension(ExtensionType.Constructor)]
public static IWidget CreateNativeWidget()
{
    // choose implementation, then...
    return new XWidget();
}

// Extension operator
// Compiler restricts the number and types of arguments and return types.
// We will probably want restrictions for enum types, static classes, generics, etc.
// The name of the member is not used, it is called like an operator.

[Extension(ExtensionType.OperatorAdd)]
public static Vector3 Add(Vector3 left, Vector3 right)
{
    return new Vector3(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
}

@gafter I am sorry if I am getting more theoretical at a time where more concrete ideas are discussed but I think the following can shed some light on the issues involved.

Equivalence

It helps a lot if we think that Type Extensibility is equivalent to a construct where a normal every-day type (the Extension Type) has a non-optional weak reference to an instance of the type it extends (the Extended Type). This binding of the two types needs to be bundled up in such a way that syntactically an instance of the Extension Type is identified with an instance of the Extended Type, and all the visible members of the Extension Type can be called via that identification.

This identification gives us three features (the first two of which to me are the core purpose of Type Extensibility):

  1. Syntactic merging of members (e.g.myExtendedTypeInstance.MyExtensionTypeProperty), this includes static members as well.
  2. An instance of the Extended Type can be passed as an instance of the Extension Type, all its base types and all its implemented interfaces with appropriate (non-user-defined) casting (e.g. (IMyExtensionTypeInterface)myExtendedTypeInstance or (ExtensionTypeBase)myExtendedTypeInstance).
    [EDIT: These casts should be implicit where appropriate]
  3. (Not necessary but possible plus it shows how the Extension Type can be used as a normal class) an instance of the Extension Type (if defined out of the context of the Extended Type) can be passed as an instance of the Extended Type, all its base types and all its implemented interfaces with appropriate(non-user-defined) casting (e.g. (IMyExtendedTypeInterface)myExtensionTypeInstance).

Looking at it this way, an extension class can be like any other normal class with its own inheritance chain (both up and down the hierarchy), interface implementations, generic type-variables etc. The only difference with a normal class is that there need to be some limitations due to the binding with the Extended Type (for example there needs to be some limitation in naming members to avoid collisions with members of the Extended Type).

My point is that all scenarios (features and limitations) can be properly studied by implementing them in today's C# language features, via the equivalence described above (the two linked types and a weak reference between them). If a scenario is possible and logically consistent today by this equivalence, it can be and should be included in the Type Extensibility features of the language.

I also agree with @HaloFour that this needs to be done via the CLR, not just compiler deception. I go even further as to state that Type Extensibility should be part of legitimate, academic OOP theory that needs to be properly formalised and that OOP languages should be making an effort to implement it.

Syntax proposals

From this point of view I suggest the following syntax for the class definition (note the keyword extensionof):

public class MyExtensionType : MyExtensionTypeBase, ISomeInterface, 
       extensionof ExtendedType { }

or

public class MyExtensionType extensionof ExtendedType : MyExtensionTypeBase, ISomeInterface { }

or

public class MyExtensionType extensionof<ExtendedType> : MyExtensionTypeBase, ISomeInterface { }

Alternatively extensionof can be used with <> or ( ).
The point of this syntax is that other than the extensionof<ExtendedType> signal, the definition of MyExtensionType is like any normal class. And this signal just tells the compiler that this class needs a weak reference to an instance of the Extended Type.

Another proposal is the keyword extension to point to the instance of Extended Type that the extension refers to from within the MyExtensionType class (similar to the base or this keyword).

Constructors and Operators

Constructors and Operators are quite interesting because they are members that are shared between the Extension and Extended Type (i.e. unlike an extension property their call is identical e.g. new ExtendedType(some parameters...) or myExtendedTypeInstance1 + myExtendedTypeInstance2). This means that some element of overridden and altered behaviour from the original could potentially be introduced.

Given the possibility that the original Extended Type constructors and operators can be called from the Extension Type (e.g. an Extended Type constructor could be called via the extension keyword similarly to base and this constructors calls), three general principles need to be considered:

  1. Allow override of Extended Type's behaviour (i.e. everything goes).
  2. Force preservation of Extended Type's behaviour without allowing extended behaviour (i.e. don't touch constructors and operators).
  3. Force preservation of Extended Type's behaviour but allow extended behaviour (i.e. yes, but...).

The first gives more freedom and leaves the developers (both creators and consumers of extensions) to deal with consequences. The advantage here is that the option to preserve is still available.
The second means constructor and operator extensions are not allowed.
The third means for example that when a constructor whose signature matches an original constructor is called then the original constructor is force-called to create the referenced Extended Type instance but no access to that instance is given during construction to avoid altering state and behaviour (i.e. the extension reference will not be available). New constructors and operators (that don't match original ones) are freely allowed in this case.

Without totally rejecting 1 and 2 it seems that 3 is closer to the idea behind Type Extensibility i.e. extending a pre-existing type's state and functionality without interfering with its original behaviour.

Case studies can be i) the Visual Studio designer working with extended controls or ii) the compiler and framework working with extended primitives (e.g. extended strings) or iii) the framework working with extended classes in general or iv) an attempt to recreate WPF in WindowsForms with extended controls.

Hope this is of some help.

Implicit extension operators would have been useful today. Looking forward to this!

The OP is missing an example of instance properties (non-auto-implemented) and never specifies that they are allowed. From the current PRs for this feature, I gather that they are. This could be made a bit clearer for people who are using this proposal to keep informed about the shape of the feature.

@grwGeo

Type Extensibility and Extension Members need to be separate issues. The Extension Members feature should enable Type Extensibility, not be the Type Extensibility feature itself.

Furthermore I suspect that getting support for type extensibility baked into the runtime is pretty unlikely, considering the DLR already does this without any special runtime support. If they couldn't make the case for it then, or deemed it unnecessary, chances of it appearing to support a C# language feature are pretty close to nil.

Fortunately, with a robust Extension Members language feature in place, it will be pretty straightforward to roll your own type extensibility mechanism.

This feature request is now tracked at dotnet/csharplang#192

Yeah sharing code in XF is a real pain because this feature does not exist. All the code in WPF, Silverlight, and UWP uses "DataContext", but the same thing in Xamarin Forms is inexplicably "BindingContext". If I could put an extension property on all BindableObjects, immediately, code sharing would be a lot easier.

@quinisext

In short, extension class is not a descendant of the extended class, so it shouldn't look like one

Why though? : as been used to show things other than inheritance in C# before.

Edit:
Firstly, there's MyClass : IMyInterface. MyClass isn't inheriting IMyInterface, it's implementing it. In Java, they use extends for classes and implements for interfaces. That's Another reason why I don't want an extends , or extensionof keyword, is that it might seem like it's inheritance for Java people.

Having public extension class : MyClass shows that this is a special type of class whereas public class extensionof MyClass might be confused with inheritence and just in general feels more like Java and less like C#.

where T : class, where T : new(), where T : struct. All of those are not things that are even remotely possible to inherit from, not in the way sealed classes can't be inherited, either, but we know what it means, and it's cleaner.

And I actually like the naming of the classes better than just using the type, it feels more like what's really going on: I'm making a new class that the compiler will treat as part of the original.

Another idea would be to add a modifier for classes that is basically the extension-everything counterpart to sealed, in that it prevents people from extending your class.

Something like public unextendable class Person would make Person not able to be extended.

@AustinBryan

Another idea would be to add a modifier for classes that is basically the extension-everything counterpart to sealed, in that it prevents people from extending your class.

Something like public unextendable class Person would make Person not able to be extended.

Why would you want to do that? this sounds like a bad idea to me because you shouldn't care whether people extending your types, it's their choice, not yours as they don't change any behaviour, they add new on top of existing one and atm the experience is unpleasant, I mean in order to "extend" types today, depends on the situation, you have to copy/paste the code, use wrappers or static classes and then at the consumer side you have to relate to some other type as opposed to the original type that was "extended" so why would you add a feature that tries to make the experience better and address this problem only to reintroduce the exact same problem?

It makes sense to seal a class or individual methods because people don't want others to modify the behaviour but it doesn't make sense to prevent others from extending it unless the person is a "control freak". 😄

Will we still be required to use the this keyword to use extension methods, properties, etc.?

Almost certainly yes

@gafter Why is that?

What's the alternative, @AustinBryan?

@paulomorgado #998 (make this optional for extensions)

@paulomorgado Make it so we don't have to use this... (obviously). There's literally no point. It's not like they need it, it was just a design choice, and a pretty annoying one. It just takes up more characters and I find it annoying. And the argument "it's not necessary to have" is very, very weak. So many C# features are not necessary, like expression bodied anything, properties, local functions, var, target-typed new, etc., all of these things can be done without them, it just makes life easier to have. Some features simply improve quality of life and this is one of them.

Just because you use this. all the time so you don't find it annoying, doesn't mean the rest of us do, as I only use this for when I have to.

@willard720, I find it informative. Can you find a situation where qualifying with this gets you in any trouble?

I'm not sure I understand the current status of this issue.
It's marked as 3 - Working and has an assignee (@MadsTorgersen), but it is also closed.
Is this still planned, either for C# 8.0 or for a future minor release? Or, is it already working in a C# 8.0 preview/internal build? 🤔
@gafter what's the official word on this?

Thanks!

@Sergio0694 language feature design is now tracked in dotnet/csharplang, this is an obsolete issue.

@orthoxerox whoops, my bad, I was sure I was already in the csharplang repo 😄
Thanks for pointing this out!
I see it's been added to the X.0 milestone, hopefully it'll be out soon!

@MadsTorgersen @Sergio0694 can you please point where in https://github.com/dotnet/csharplang/milestones is this #11159 feature located. I cannot find it.

OK I found it dotnet/csharplang#192

Please do this as soon as possible!

Why we still require to name the extension class while it can be self explanatory like:

public extension int { }
// instead of
public extension class IntegerExtensions : int { } 

Using class-base to specify the target type can get weird in case of generic, static or array types:

public extension class IntegerArrayExtensions : int[] { }
// for defining static extension methods ..
public extension class ConsoleExtensions : Console { } 
// would this be allowed?
public extension class GenericExtensions<T> : T /* where T : ... */ { }

Alternatively,

public extension int[] { }
public extension Console { }
public extension<T> T /* where T : ... */  { }
public extension<T> List<List<T>> { }

This would reserve class-base for when we have virtual extension methods (#258) to add interface implementations to existing types (#8127).

we can no longer call our extension member if it became a official implement

all the other projects for "extension everything" have been closed.

Is it possible to revive the project?

Thanks!

@Elviszip

all the other projects for "extension everything" have been closed.

Most issues have been moved to discussions until they have more of a concrete proposal. I think this is the relevant discussion for this topic: dotnet/csharplang#5498

Is it possible to revive the project?

There is currently a working group looking into roles and extensions together as a project, so it is alive and well:

https://github.com/dotnet/csharplang/tree/main/meetings/working-groups/roles

With an evolving proposal here: https://github.com/jcouv/csharplang/blob/roles-spec/proposals/roles.md