{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/roslyn/issues/11159","repository_url":"https://api.github.com/repos/dotnet/roslyn","labels_url":"https://api.github.com/repos/dotnet/roslyn/issues/11159/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/roslyn/issues/11159/comments","events_url":"https://api.github.com/repos/dotnet/roslyn/issues/11159/events","html_url":"https://github.com/dotnet/roslyn/issues/11159","id":153598385,"number":11159,"title":"Language Feature: Extension Everything","user":{"login":"gafter","id":3804346,"avatar_url":"https://avatars.githubusercontent.com/u/3804346?v=3","gravatar_id":"","url":"https://api.github.com/users/gafter","html_url":"https://github.com/gafter","followers_url":"https://api.github.com/users/gafter/followers","following_url":"https://api.github.com/users/gafter/following{/other_user}","gists_url":"https://api.github.com/users/gafter/gists{/gist_id}","starred_url":"https://api.github.com/users/gafter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gafter/subscriptions","organizations_url":"https://api.github.com/users/gafter/orgs","repos_url":"https://api.github.com/users/gafter/repos","events_url":"https://api.github.com/users/gafter/events{/privacy}","received_events_url":"https://api.github.com/users/gafter/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/dotnet/roslyn/labels/Area-Language%20Design","name":"Area-Language Design","color":"5319e7"},{"url":"https://api.github.com/repos/dotnet/roslyn/labels/Feature%20Request","name":"Feature Request","color":"fc2929"},{"url":"https://api.github.com/repos/dotnet/roslyn/labels/Language-C%23","name":"Language-C#","color":"207de5"},{"url":"https://api.github.com/repos/dotnet/roslyn/labels/New%20Language%20Feature%20-%20Extension%20Everything","name":"New Language Feature - Extension Everything","color":"bfd4f2"}],"state":"open","locked":false,"assignee":{"login":"MadsTorgersen","id":6677272,"avatar_url":"https://avatars.githubusercontent.com/u/6677272?v=3","gravatar_id":"","url":"https://api.github.com/users/MadsTorgersen","html_url":"https://github.com/MadsTorgersen","followers_url":"https://api.github.com/users/MadsTorgersen/followers","following_url":"https://api.github.com/users/MadsTorgersen/following{/other_user}","gists_url":"https://api.github.com/users/MadsTorgersen/gists{/gist_id}","starred_url":"https://api.github.com/users/MadsTorgersen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MadsTorgersen/subscriptions","organizations_url":"https://api.github.com/users/MadsTorgersen/orgs","repos_url":"https://api.github.com/users/MadsTorgersen/repos","events_url":"https://api.github.com/users/MadsTorgersen/events{/privacy}","received_events_url":"https://api.github.com/users/MadsTorgersen/received_events","type":"User","site_admin":false},"assignees":[{"login":"MadsTorgersen","id":6677272,"avatar_url":"https://avatars.githubusercontent.com/u/6677272?v=3","gravatar_id":"","url":"https://api.github.com/users/MadsTorgersen","html_url":"https://github.com/MadsTorgersen","followers_url":"https://api.github.com/users/MadsTorgersen/followers","following_url":"https://api.github.com/users/MadsTorgersen/following{/other_user}","gists_url":"https://api.github.com/users/MadsTorgersen/gists{/gist_id}","starred_url":"https://api.github.com/users/MadsTorgersen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MadsTorgersen/subscriptions","organizations_url":"https://api.github.com/users/MadsTorgersen/orgs","repos_url":"https://api.github.com/users/MadsTorgersen/repos","events_url":"https://api.github.com/users/MadsTorgersen/events{/privacy}","received_events_url":"https://api.github.com/users/MadsTorgersen/received_events","type":"User","site_admin":false}],"milestone":null,"comments":49,"created_at":"2016-05-07T14:47:03Z","updated_at":"2016-07-22T05:22:51Z","closed_at":null,"body":"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.\r\n\r\nExtension Everything for C#\r\n======\r\n\r\nThis 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.\r\n\r\nHere is an example that summarizes the new proposed syntax forms:\r\n\r\n```cs\r\n// \"extension\" is a new (contextual keyword) modifier, permitted before the keyword \"class\".\r\n// It can be combined with \"partial\". It produces a static class that one would not normally use\r\n// directly, however there will be syntax forms for directly using all of the named members\r\n// (but not the operators) as members of this class.\r\npublic extension class ListListExtensions<T>\r\n  : List<List<T>> // the \"extends\" clause names the extended type. May be an interface\r\n  // , Interface  // we do not permit adding interfaces in the base clause\r\n{\r\n   // static variables are no problem, and hosted in the container ListListExtensions<T>\r\n   private static int _flattenCount = 0;\r\n\r\n   // static methods are simply hosted in the container, ListListExtensions<T> in this case\r\n   public static int GetFlattenCount()\r\n   {\r\n     return _flattenCount;\r\n   }\r\n\r\n   // static properties are similarly hosted in the container.\r\n   // They translate into a static \"indexed\" property, indexed with a \"this\" (extension) parameter.\r\n   public static int FlattenCount\r\n   {\r\n       get\r\n       {\r\n         return _flattenCount; // extension static variable access\r\n       }\r\n   }\r\n\r\n   // instance methods are compiled using the same pattern as existing instance extensions.\r\n   // The additional hidden parameter is named @this from the SemanticModel's point of view.\r\n   public List<T> Flatten()\r\n   {\r\n     // Within instance members, \"this\" is of type List<List<T>>\r\n     _flattenCount++;\r\n     ...\r\n   }\r\n   \r\n   // Extension indexers permitted, both static and instance.\r\n   public List<List<T>> this[int[] indices] => ...;\r\n   \r\n   // We do not support instance extension fields, but we could support them in the future\r\n   // if the type being extended is a reference type, using ConditionalWeakTable.\r\n   // public int InstanceMember;\r\n\r\n   // We do not support constructors, but we could support them in the future\r\n   // by creating an ordinary method with a special name. Some design work would\r\n   // be required to ensure the following syntax is possible, or to design an alternative.\r\n   // public List<List<T>>(int defaultSize) : this(...) { ... }\r\n\r\n   // Operators are permitted. Operators are required to have a parameter (or return type,\r\n   // for implicit conversions) of the extended type.\r\n   // All of the lookup rules for operators will need to be amended to look in extension types\r\n   // that are in scope.\r\n   public static implicit operator List<T>(List<List<T>> self)\r\n   {\r\n     return self.Flatten();\r\n   }\r\n   \r\n   public static implicit List<List<T>> operator +(List<List<T>> left, List<List<T>> right) => left.Concat(right);\r\n}\r\n```\r\n\r\nLimitations:\r\n- Instance fields not permitted (at first)\r\n- Constructors not permitted (at first)\r\n- Events not permitted (at first)\r\n- The base clause cannot add interfaces\r\n- No members may be virtual, override, abstract, sealed, etc.\r\n- Private helper members (e.g. `_flattenCount` above) work the same as extension members, even though not accessible outside.\r\n- 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.\r\n- No instance auto-properties (until we support instance fields).\r\n\r\nSubstantial LDM design work will be required\r\n- What is the syntax?\r\n- What is allowed and not allowed? Extending int[]?\r\n- 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.\r\n- Every construct that can use one of these will have to have its spec amended\r\n  - Member access, method overload resolution\r\n  - Indexing\r\n  - Every kind of operator (e.g. what are the constraints at the declaration, where do we look on the use)\r\n  - Later, constructors\r\n- For conversions, what limitations do we need to not run into trouble?\r\n"},"comment":{"url":"https://api.github.com/repos/dotnet/roslyn/issues/comments/234457115","html_url":"https://github.com/dotnet/roslyn/issues/11159#issuecomment-234457115","issue_url":"https://api.github.com/repos/dotnet/roslyn/issues/11159","id":234457115,"user":{"login":"andrew-skybound","id":1355174,"avatar_url":"https://avatars.githubusercontent.com/u/1355174?v=3","gravatar_id":"","url":"https://api.github.com/users/andrew-skybound","html_url":"https://github.com/andrew-skybound","followers_url":"https://api.github.com/users/andrew-skybound/followers","following_url":"https://api.github.com/users/andrew-skybound/following{/other_user}","gists_url":"https://api.github.com/users/andrew-skybound/gists{/gist_id}","starred_url":"https://api.github.com/users/andrew-skybound/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/andrew-skybound/subscriptions","organizations_url":"https://api.github.com/users/andrew-skybound/orgs","repos_url":"https://api.github.com/users/andrew-skybound/repos","events_url":"https://api.github.com/users/andrew-skybound/events{/privacy}","received_events_url":"https://api.github.com/users/andrew-skybound/received_events","type":"User","site_admin":false},"created_at":"2016-07-22T05:22:51Z","updated_at":"2016-07-22T05:22:51Z","body":"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.\r\n\r\nI 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.\r\n\r\nI'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.\r\n\r\nSo, 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.\r\n\r\nLike 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.\r\n\r\nHere is the code:\r\n\r\n```C#\r\n// Property getters and setters\r\n// Compiler restricts the number of arguments and return types.\r\n// When accessed as a property, the \"Get\" and \"Set\" prefixes are omitted just like the\r\n//   \"Attribute\" suffix is omitted from attribute classes.\r\n// There is no compiler-generated storage mechanism and no \"auto\" extension properties.\r\n\r\n[Extension(ExtensionType.GetProperty)]\r\npublic static string GetTrimmedText(this Control control)\r\n{\r\n\treturn control.Text.Trim();\r\n}\r\n\r\n[Extension(ExtensionType.SetProperty)]\r\npublic static void SetTrimmedText(this Control control, string value)\r\n{\r\n\tcontrol.Text = value.Trim();\r\n}\r\n\r\n// Event accessors\r\n// Compiler restricts the number of arguments and the return type must be void.\r\n// When accessed as an event, the \"Add\" and \"Remove\" prefixes are omitted.\r\n\r\n[Extension(ExtensionType.AddEvent)]\r\npublic static void AddMouseTripleClick(this Control control, MouseEventHandler e)\r\n{\r\n\t// ... etc\r\n\tcontrol.Events.AddHandler(\"MouseTripleClick\", e);\r\n}\r\n\r\n[Extension(ExtensionType.RemoveEvent)]\r\npublic static void RemoveMouseTripleClick(this Control control, MouseEventHandler e)\r\n{\r\n\t// ... etc\r\n\tcontrol.Events.RemoveHandler(\"MouseTripleClick\", e);\r\n}\r\n\r\n// Static extension members are declared without a \"this\" argument, and by providing\r\n// an additional parameter to the ExtensionAttribute to say which type will receive the member.\r\n// This usage should work for all kinds of members (not just methods).\r\n\r\n[Extension(typeof(Color))]\r\npublic static Color FromHwb(double hue, double whiteness, double blackness, double alpha)\r\n{\r\n\t// convert HWB to RGB ...\r\n\treturn Color.FromArgb(...);\r\n}\r\n\r\n// Extension constructor\r\n// The return type determines the type for which the constructor is being declared.\r\n// We will probably want some restrictions here for enum types, static classes, etc.\r\n// The name of this member is not used, it is called like `new ReturnType(...)`.\r\n\r\n[Extension(ExtensionType.Constructor)]\r\npublic static IWidget CreateNativeWidget()\r\n{\r\n\t// choose implementation, then...\r\n\treturn new XWidget();\r\n}\r\n\r\n// Extension operator\r\n// Compiler restricts the number and types of arguments and return types.\r\n// We will probably want restrictions for enum types, static classes, generics, etc.\r\n// The name of the member is not used, it is called like an operator.\r\n\r\n[Extension(ExtensionType.OperatorAdd)]\r\npublic static Vector3 Add(Vector3 left, Vector3 right)\r\n{\r\n\treturn new Vector3(left.X + right.X, left.Y + right.Y, left.Z + right.Z);\r\n}\r\n```"}}
{"actor":{"display_login":"andrew-skybound"}}