{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/roslyn/issues/5292","labels_url":"https://api.github.com/repos/dotnet/roslyn/issues/5292/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/roslyn/issues/5292/comments","events_url":"https://api.github.com/repos/dotnet/roslyn/issues/5292/events","html_url":"https://github.com/dotnet/roslyn/issues/5292","id":106883055,"number":5292,"title":"[Proposal] add supersede modifier to enable more tool generated code scenarios.","user":{"login":"mattwar","id":3387305,"avatar_url":"https://avatars.githubusercontent.com/u/3387305?v=3","gravatar_id":"","url":"https://api.github.com/users/mattwar","html_url":"https://github.com/mattwar","followers_url":"https://api.github.com/users/mattwar/followers","following_url":"https://api.github.com/users/mattwar/following{/other_user}","gists_url":"https://api.github.com/users/mattwar/gists{/gist_id}","starred_url":"https://api.github.com/users/mattwar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mattwar/subscriptions","organizations_url":"https://api.github.com/users/mattwar/orgs","repos_url":"https://api.github.com/users/mattwar/repos","events_url":"https://api.github.com/users/mattwar/events{/privacy}","received_events_url":"https://api.github.com/users/mattwar/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"}],"state":"open","locked":false,"assignee":{"login":"mattwar","id":3387305,"avatar_url":"https://avatars.githubusercontent.com/u/3387305?v=3","gravatar_id":"","url":"https://api.github.com/users/mattwar","html_url":"https://github.com/mattwar","followers_url":"https://api.github.com/users/mattwar/followers","following_url":"https://api.github.com/users/mattwar/following{/other_user}","gists_url":"https://api.github.com/users/mattwar/gists{/gist_id}","starred_url":"https://api.github.com/users/mattwar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mattwar/subscriptions","organizations_url":"https://api.github.com/users/mattwar/orgs","repos_url":"https://api.github.com/users/mattwar/repos","events_url":"https://api.github.com/users/mattwar/events{/privacy}","received_events_url":"https://api.github.com/users/mattwar/received_events","type":"User","site_admin":false},"milestone":{"url":"https://api.github.com/repos/dotnet/roslyn/milestones/8","html_url":"https://github.com/dotnet/roslyn/milestones/C%23%207%20and%20VB%2015","labels_url":"https://api.github.com/repos/dotnet/roslyn/milestones/8/labels","id":962632,"number":8,"title":"C# 7 and VB 15","description":"\r\n\r\n<!---\r\n@huboard:{\"order\":0.375}\r\n-->\r\n","creator":{"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},"open_issues":54,"closed_issues":9,"state":"open","created_at":"2015-02-02T23:41:34Z","updated_at":"2015-09-17T00:49:57Z","due_on":null,"closed_at":null},"comments":3,"created_at":"2015-09-16T23:14:20Z","updated_at":"2015-09-17T15:05:49Z","closed_at":null,"body":"Some C# language features exist primarily for tooling scenarios. Designers, build time tools and other code generators produce source code that compiles together with and augments user written code.  Partial classes and partial method are two of these.\r\n\r\nWith partial classes, a tool can generate source files that make additional declarations for a class, adding members, interfaces and attributes on top of what the user wrote.\r\n\r\nPartial methods allow tools to declare signatures for methods that don’t actually exist, but that can be supplied elsewhere by the user. In this way the tool is free to generate code invoking this partial method without requiring the method to have been fully supplied. They are limited to private void returning methods so that they can vanish during compilation if the user has not supplied a full declaration with a body for the partial method.\r\n\r\nIn this way, partial methods are a clever trick enabling tool generated code to interact with user written code in more than just the simple additive way that partial classes provide.  However, it is a very narrow trick.  It allows the user to enhance the behavior of the tool generated code at well-defined spots by providing method implementations, but it does not allow the tool generated code to enhance the behavior of the user written code.\r\n\r\nFor example, imagine I wanted a tool to generate code that adds entry and exit logging to my user written code (without actually changing my source files or post processing the binary.)  I could pervert the use of partial methods to approximate this, by inverting how they are normally used.  I could pre-establish an agreement between my code and the tool (probably established by the tool), and liberally sprinkle invocations to logging methods throughout my code base, and then let the tool generate the bodies.\r\n\r\n```cs\r\n// user written code\r\npublic partial class MyClass\r\n{\r\n    private partial void log_entry(string name);\r\n    private partial void log_exit(string name);\r\n\r\n     public void Method1() \r\n     {\r\n          log_entry(“Method1”); \r\n          // something totally awesome here\r\n          log_exit(“Method1”);\r\n     }\r\n}\r\n\r\n// tool generated code\r\npublic partial class MyClass\r\n{\r\n    private partial void log_entry(string name)\r\n    {\r\n         Console.WriteLine(“entered {0}”, name);\r\n    }\r\n    private partial void log_exit(string name);\r\n    {\r\n        Console.WriteLIne(“exited {0}”, name);\r\n    }\r\n}\r\n```\r\n\r\nThen I could choose to run the tool or not, so that the calls to log_entry and log_exit either had an implementation at runtime or not.  \r\n\r\nBut I would have to manually place calls to these methods everywhere throughout my code.  And I’d have to do this over and over again for each additional tool that needed to interact with my code.  A very tedious prospect, and one better suited for tools generating code than my tired fingers.  \r\n\r\nA better solution would be one that allowed the generated code to enhance the user written code directly, by replacing it or overriding it, and placing all the burden of declaration into the tool generated code.\r\n\r\n## Proposal: Superseding members\r\nThe supersedes feature enables just this.  It allows class members to override members defined in the same class, by superseding their declaration with a new declaration using the supersede declaration modifier.  \r\n\r\n```cs\r\n// user written code\r\npublic partial class MyClass\r\n{\r\n      public void Method1()\r\n      {\r\n          // something totally awesome here\r\n      }\r\n}\r\n\r\n// tool generated code\r\npublic partial class MyClass\r\n{\r\n      public supersede void Method1()\r\n      {\r\n           Console.WriteLine(“entered Method1”);\r\n           superseded();\r\n           Consolel.WriteLine(“exited Method1”);\r\n      }\r\n}\r\n```\r\n\r\nIn this scenario the tool generated Method1 supersedes the user written Method1.  All invocations are directed to the tool generated method.  The tool generated implementation can then invoke the user written method using the superseded keyword that points at the member that was superseded by this declaration.\r\n\r\nUsing this technique, I could have a tool generate my boilerplate INotifyPropertyChanged implementations.\r\n\r\n```cs\r\n// user written code\r\npublic partial class MyClass\r\n{\r\n     public int Property { get; set; }\r\n}\r\n\r\n// tool generated code\r\npublic partial class MyClass  : INotifyPropertyChanged\r\n{\r\n     public event PropertyChangedEventHandler PropertyChanged;\r\n\r\n     public supersede int Property\r\n     {\r\n          get { return superseded; }\r\n          set\r\n          {\r\n              if (superseded != value)\r\n              {\r\n                    superseded = value;\r\n                    var ev = this.PropertyChanged;\r\n                    if (ev != null) ev(this, new PropertyChangedEventArgs(“Property”));\r\n              }\r\n          }\r\n     } \r\n}\r\n```\r\n\r\n## Multiple Generators\r\nOf course, since the primary scenario for this feature is tool generated code and there is likely going to be times where there is more than one tool wanting to supersede a user written member, some mechanism should exist that allows one bit of tool generated code to supersede another bit of tool generated code without requiring them to know about each other.\r\n\r\n## Proposal: superseding superseded members\r\nWe can allow more than one superseded member declaration to exist at the same time.  One of the superseding members directly supersedes the original member.  Another superseding member then supersedes the prior superseding member, and so on.  Thus a chain of superseding members exists similar to how multiple overrides might exist on the same member in an inheritance hierarchy.  In fact, it is somewhat helpful to think of superseding as directly comparable to overriding.  If you imagine a series of overrides of a member in a vertical inheritance hierarchy, you could image superseding as kind of a horizontal overriding (between partial class declarations).\r\n\r\n```cs\r\n// user written code\r\npublic partial class MyClass\r\n{\r\n      public void Method1()\r\n      {\r\n          // something totally awesome here\r\n      }\r\n}\r\n\r\n// tool #1 generated code\r\npublic partial class MyClass\r\n{\r\n      public supersede void Method1()\r\n      {\r\n           // do something interesting\r\n           superseded();\r\n      }\r\n}\r\n\r\n// tool #2 generated code\r\npublic partial class MyClass\r\n{\r\n      public supersede void Method1()\r\n      {\r\n           // do something else interesting\r\n           superseded();\r\n      }\r\n}\r\n```\r\n\r\n## Order of supersession\r\nThe only problem we have is determine which superseding member is the first, the next and so on. The order is going to matter, because it determines which gets called first and that can affect behavior of the entire composition.\r\n\r\nUnlike member overrides that occur in derived classes that specifically call out the names of the classes they are deriving from, the supersede syntax doesn’t directly identify the precise member that is being superseded.  And that’s a good thing, because if it did, then code generating tools would absolutely need to know about each other, so they could reference each other’s declarations.\r\n\r\nInstead, we need a different technique (other than naming) to identify ordering between the superseded members.\r\n\r\n## Proposal: lexical order \r\nWe can use lexical order to determine the order in which members are superseded.  (For multiple source files, consider the files concatenated in the order supplied to the compiler.)\r\n\r\n1.\tThe member without the supersede modifier is the base implementation.\r\n2.\tThe superseding member that is earliest in lexical order is the first to supersede the base.\r\n3.\tThe superseding member that is next in lexical order supersedes the previous superseding member, and so on.\r\n\r\nIn this way, no additional syntax needs to be invented to support having multiple superseding members for a single base member.  The natural order of their declaration determine the order for superseding.\r\n\r\n(This does not consider how the generated source files are given their order.)\r\n"},"comment":{"url":"https://api.github.com/repos/dotnet/roslyn/issues/comments/141115495","html_url":"https://github.com/dotnet/roslyn/issues/5292#issuecomment-141115495","issue_url":"https://api.github.com/repos/dotnet/roslyn/issues/5292","id":141115495,"user":{"login":"paulomorgado","id":470455,"avatar_url":"https://avatars.githubusercontent.com/u/470455?v=3","gravatar_id":"","url":"https://api.github.com/users/paulomorgado","html_url":"https://github.com/paulomorgado","followers_url":"https://api.github.com/users/paulomorgado/followers","following_url":"https://api.github.com/users/paulomorgado/following{/other_user}","gists_url":"https://api.github.com/users/paulomorgado/gists{/gist_id}","starred_url":"https://api.github.com/users/paulomorgado/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/paulomorgado/subscriptions","organizations_url":"https://api.github.com/users/paulomorgado/orgs","repos_url":"https://api.github.com/users/paulomorgado/repos","events_url":"https://api.github.com/users/paulomorgado/events{/privacy}","received_events_url":"https://api.github.com/users/paulomorgado/received_events","type":"User","site_admin":false},"created_at":"2015-09-17T15:05:49Z","updated_at":"2015-09-17T15:05:49Z","body":"@mattwar,  besides allowing multiple supreseeded implementations of the method, what else can you do with this that you can't with the current implementation?"}}