{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/csharplang/issues/1568","repository_url":"https://api.github.com/repos/dotnet/csharplang","labels_url":"https://api.github.com/repos/dotnet/csharplang/issues/1568/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/csharplang/issues/1568/comments","events_url":"https://api.github.com/repos/dotnet/csharplang/issues/1568/events","html_url":"https://github.com/dotnet/csharplang/issues/1568","id":326766867,"number":1568,"title":"[Proposal] Allow overriding single accessor properties with multiple accessors","user":{"login":"MrJul","id":1623034,"avatar_url":"https://avatars3.githubusercontent.com/u/1623034?v=4","gravatar_id":"","url":"https://api.github.com/users/MrJul","html_url":"https://github.com/MrJul","followers_url":"https://api.github.com/users/MrJul/followers","following_url":"https://api.github.com/users/MrJul/following{/other_user}","gists_url":"https://api.github.com/users/MrJul/gists{/gist_id}","starred_url":"https://api.github.com/users/MrJul/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MrJul/subscriptions","organizations_url":"https://api.github.com/users/MrJul/orgs","repos_url":"https://api.github.com/users/MrJul/repos","events_url":"https://api.github.com/users/MrJul/events{/privacy}","received_events_url":"https://api.github.com/users/MrJul/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":2,"created_at":"2018-05-26T20:59:13Z","updated_at":"2018-05-27T17:31:37Z","closed_at":null,"author_association":"NONE","body":"Copied and improved from the original issue https://github.com/dotnet/roslyn/issues/9482\r\n\r\n---\r\n\r\n## Description\r\n\r\nCurrently, a `virtual` or `abstract` property having a single `get` or `set` accessor can't be overridden with a property having both accessors. This proposal removes this limitation.\r\n\r\nHow the override is written doesn't matter: explicit accessors as well as auto-properties are permitted.\r\n\r\nThe following code is now valid:\r\n\r\n```\r\npublic abstract class Base {\r\n    public abstract string Prop1 { get; } // can also be virtual\r\n    public abstract string Prop2 { set; } // can also be virtual\r\n}\r\n\r\npublic class Derived : Base {\r\n    public override string Prop1 { get; set; } // no more CS0546\r\n    public override string Prop2 {\r\n        get { ... } // no more CS0545\r\n        set { ... }\r\n    }\r\n}\r\n```\r\n## Why\r\n1. This kind of construct is already allowed for interfaces: a read-only or write-only property declared in an interface can be implemented by a read/write property. This proposal makes this valid for classes to avoid disparities.\r\n2. It's a valid CLR construct. Lately, the C# compiler had other proposals to expose existing CLR features to C# (notably `private protected`).\r\n3. Because it's useful ;) Here's a sketch of the piece of code that made me write this proposal in the first place: \r\n\r\n```\r\npublic class RenderBlock {\r\n    public abstract int Width { get; }\r\n}\r\n\r\npublic class AutoSizeText : RenderBlock {\r\n    public string Text { get; set;}\r\n    public override int Width => CalculateWidthFromText();\r\n}\r\n\r\npublic class Rectangle : RenderBlock {\r\n    public override int Width { get; set; }\r\n}\r\n```\r\n\r\nOf course I could have added a SetWidth() method, but this didn't feel right.\r\n(Note: as with any code problem, this can be - and has been - solved differently: let's not discuss that, it's just a sample.)\r\n## Impact\r\n### CLR\r\n\r\nThe CLR already allows this. As far as it's concerned, the two accessors are separate methods, and the override/abstract/virtual modifiers are applied on each method, not on the property itself.\r\n\r\nI've compiled a code equivalent to the sample above using ilasm, verified that it's valid according to peverify, and ran it successfully.\r\n### Compiler\r\n\r\nThe following compiler errors are removed:\r\n- CS0545: cannot override because 'property' does not have an overridable get accessor\r\n- CS0546: cannot override because 'property' does not have an overridable set accessor\r\n\r\nI've already made some changes in a private branch, those errors are pretty straightforward to remove. No changes are needed in code generation. I'll add proper tests and send a pull request if this proposal is accepted.\r\n### Compatibility\r\n\r\nThe current compiler and tooling can already handle assemblies using those overrides.\r\nSince it's always been a valid construct at the metadata level, I suspect pre-Roslyn versions of the compiler have no problem handling this, but I've not tested them.\r\n### Implicit implications\r\n\r\nThe newly added accessor is automatically marked as `virtual`, unless the property or its containing class is `sealed`. This seems like a sensible choice, since `override` already implies `virtual`.\r\n## Spec change\r\n\r\nIn the [classes specification](https://github.com/dotnet/csharplang/blob/master/spec/classes.md#virtual-sealed-override-and-abstract-property-accessors), the following text:\r\n\r\n> If the inherited property has only a single accessor (i.e., if the inherited property is read-only or write-only), the overriding property must include only that accessor.\r\n\r\nis replaced by:\r\n\r\n> If the inherited property has only a single accessor (i.e., if the inherited property is read-only or write-only), the overriding property must include that accessor, and can optionally define another accessor. If a new accessor is introduced by the overriding property declaration, it is automatically considered `virtual`, unless the property is `sealed`.\r\n"},"comment":{"url":"https://api.github.com/repos/dotnet/csharplang/issues/comments/392349489","html_url":"https://github.com/dotnet/csharplang/issues/1568#issuecomment-392349489","issue_url":"https://api.github.com/repos/dotnet/csharplang/issues/1568","id":392349489,"user":{"login":"svick","id":287848,"avatar_url":"https://avatars3.githubusercontent.com/u/287848?v=4","gravatar_id":"","url":"https://api.github.com/users/svick","html_url":"https://github.com/svick","followers_url":"https://api.github.com/users/svick/followers","following_url":"https://api.github.com/users/svick/following{/other_user}","gists_url":"https://api.github.com/users/svick/gists{/gist_id}","starred_url":"https://api.github.com/users/svick/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/svick/subscriptions","organizations_url":"https://api.github.com/users/svick/orgs","repos_url":"https://api.github.com/users/svick/repos","events_url":"https://api.github.com/users/svick/events{/privacy}","received_events_url":"https://api.github.com/users/svick/received_events","type":"User","site_admin":false},"created_at":"2018-05-27T17:31:37Z","updated_at":"2018-05-27T17:31:37Z","author_association":"CONTRIBUTOR","body":"@HaloFour You don't have to point it to a method in the base class, you point it to the method that's in the derived class. [That's already what happens when you `override` a property](https://sharplab.io/#v2:C4LglgNgPghgRgZ2AJxgY2AAgAICZMBCMCApgLABQA3pZnTgMybxKoY4CMADJgArIB7AA6YqmAOYlgAbkwBfSgoqU8mACIlkYAG4kAJphCFi5arXrYmA3ci16SnHv2GiJUzAF4AfJgB2AVwgIWSUlIA=) and I don't see why would it cause any issues with this proposal.\r\n\r\nIn other words, this code:\r\n\r\n```c#\r\npublic abstract class Base\r\n{\r\n    public abstract string Prop { get; }\r\n}\r\n\r\npublic class Derived : Base\r\n{\r\n    public override string Prop\r\n    {\r\n        get => null;\r\n        set {}\r\n    }\r\n}\r\n```\r\n\r\nwould produce the following IL, which peverifies fine.\r\n\r\n```il\r\n.assembly extern mscorlib { }\r\n.assembly lib { }\r\n\r\n.class public abstract auto ansi beforefieldinit Base\r\n       extends [mscorlib]System.Object\r\n{\r\n  .method public hidebysig newslot specialname abstract virtual \r\n          instance string  get_Prop() cil managed\r\n  {\r\n  }\r\n\r\n  .method family hidebysig specialname rtspecialname \r\n          instance void  .ctor() cil managed\r\n  {\r\n    ldarg.0\r\n    call       instance void [mscorlib]System.Object::.ctor()\r\n    ret\r\n  }\r\n\r\n  .property instance string Prop()\r\n  {\r\n    .get instance string Base::get_Prop()\r\n  }\r\n}\r\n\r\n.class public auto ansi beforefieldinit Derived\r\n       extends Base\r\n{\r\n  .method public hidebysig specialname virtual \r\n          instance string  get_Prop() cil managed\r\n  {\r\n    ldnull\r\n    ret\r\n  }\r\n\r\n  .method public hidebysig specialname virtual \r\n          instance void  set_Prop(string 'value') cil managed\r\n  {\r\n    ret\r\n  }\r\n\r\n  .method public hidebysig specialname rtspecialname \r\n          instance void  .ctor() cil managed\r\n  {\r\n    ldarg.0\r\n    call       instance void Base::.ctor()\r\n    ret\r\n  }\r\n\r\n  .property instance string Prop()\r\n  {\r\n    .get instance string Derived::get_Prop()\r\n    .set instance void Derived::set_Prop(string)\r\n  }\r\n}\r\n```"}}
{"actor":{"display_login":"svick"}}