{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/csharplang/issues/288","repository_url":"https://api.github.com/repos/dotnet/csharplang","labels_url":"https://api.github.com/repos/dotnet/csharplang/issues/288/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/csharplang/issues/288/comments","events_url":"https://api.github.com/repos/dotnet/csharplang/issues/288/events","html_url":"https://github.com/dotnet/csharplang/issues/288","id":215243291,"number":288,"title":"A Tour of Default Interface Methods for C# (\"traits\")","user":{"login":"gafter","id":3804346,"avatar_url":"https://avatars0.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":[{"id":537024200,"url":"https://api.github.com/repos/dotnet/csharplang/labels/Discussion","name":"Discussion","color":"c2e0c6","default":false},{"id":513192173,"url":"https://api.github.com/repos/dotnet/csharplang/labels/Proposal","name":"Proposal","color":"fbca04","default":false}],"state":"open","locked":false,"assignee":{"login":"gafter","id":3804346,"avatar_url":"https://avatars0.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},"assignees":[{"login":"gafter","id":3804346,"avatar_url":"https://avatars0.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}],"milestone":null,"comments":191,"created_at":"2017-03-19T05:38:22Z","updated_at":"2017-03-20T21:11:53Z","closed_at":null,"body":"This is an explanatory summary of the [proposed](https://github.com/dotnet/csharplang/issues/52) [*default interface methods*](https://github.com/dotnet/csharplang/blob/master/proposals/default-interface-methods.md) feature for C#, intended to lead the LDM through an understanding of the proposed feature, with examples, and to guide the discussion. We present the feature as it applies to *methods*, but the intent is that is also applies to *properties* and *indexers*. For simplicity of exposition, we confine our discussion to methods.\r\n\r\n> Open Issue: Should this also apply to *events*?\r\n\r\nSimilarly, it applies equally to classes and structs, but we confine our exposition to classes.\r\n\r\n## Modifiers in interfaces\r\n\r\nBecause this proposal includes modifiers that can newly be applied to methods in interfaces (`private`, `static`, and `override`), as we will describe later, we propose that the default modifiers `public` and `abstract` be permitted to be explicit as well. For clarity, we sometimes use these modifiers explicitly in examples of this feature.\r\n\r\n> Open Issue: should we permit the modifiers `abstract` and `public` on methods in interfaces, even though that is the default?\r\n> Open issue: should we permit `virtual`?\r\n\r\n## Concrete methods in interfaces\r\n\r\nThe simplest form of this feature is the ability to declare a *concrete method* in an interface, which is a method with a body.\r\n\r\n``` c#\r\ninterface IA\r\n{\r\n    void M() { WriteLine(\"IA.M\"); }\r\n}\r\n```\r\n\r\nA class that implements this interface need not implement its concrete method.\r\n\r\n``` c#\r\nclass C : IA { } // OK\r\n\r\nIA i = new C();\r\ni.M(); // prints \"IA.M\"\r\n```\r\n\r\nThe final override for `IA.M` in class `C` is the concrete method `M` declared in `IA`. Note that a class does not inherit members from its interfaces; that is not changed by this feature:\r\n\r\n``` c#\r\nnew C().M(); // error: class 'C' does not contain a member 'M'\r\n```\r\n\r\nThe basic feature is particularly useful to enable evolution of existing interface types by the addition of new virtual methods.\r\n\r\n## Overrides in interfaces\r\n\r\nAn interface can `override` a method declared in a base interface, with or without explicitly naming the overridden method's declaring interface\r\n\r\n``` c#\r\ninterface IA\r\n{\r\n    void M() { WriteLine(\"IA.M\"); }\r\n}\r\ninterface IB : IA\r\n{\r\n    override void IA.M() { WriteLine(\"IB.M\"); } // explicitly named\r\n}\r\ninterface IC : IA\r\n{\r\n    override void M() { WriteLine(\"IC.M\"); } // implicitly named\r\n}\r\n```\r\n\r\nIf the interface is not named in the override declaration, then all matching methods (from direct or indirect base interfaces) are overridden. There must be at least one such method or the override declaration is an error.\r\n\r\n> Open issue: should that \"direct and indirect\" be \"direct\" here?\r\n\r\nOverrides in interfaces are useful to provide a more specific (e.g. more efficient) implementation of a base interface's method. For example, a new `Count` property on `IEnumerable` may have a much more efficient implementation on the interface `IList`.\r\n\r\nA method declared in an interface is never treated as an `override` of another method unless it contains he `override` modifier. This is necessary for compatibility.\r\n\r\n``` c#\r\ninterface IA\r\n{\r\n    void M();\r\n}\r\ninterface IB : IA\r\n{\r\n    void M(); // not related to 'IA.M'; not an override\r\n}\r\n```\r\n\r\n## Reabstraction\r\n\r\nA virtual (concrete) method declared in an interface may be overridden to be abstract in a derived interface\r\n\r\n``` c#\r\ninterface IA\r\n{\r\n    void M() { WriteLine(\"IA.M\"); }\r\n}\r\ninterface IB : IA\r\n{\r\n    override abstract void M();\r\n}\r\nclass C : IB { } // error: class 'C' does not implement 'IA.M'.\r\n```\r\n\r\nThe `abstract` modifier is not required in the declaration of `IB.M` (that is the default in interfaces), but it is probably good practice to be explicit in an override declaration.\r\n\r\nThis is useful in derived interfaces where the default implementation of a method is inappropriate and a more appropriate implementation should be provided by implementing classes.\r\n\r\n## The most specific override rule\r\n\r\nWe require that every interface and class have a *most specific override* for every interface method among the overrides appearing in the type or its direct and indirect interfaces. If there is no override, the method itself is considered the most specific override. One override `M1` is considered *more specific* than another override `M2` if `M1` is declared on type `T1`, `M2` is declared on type `T2`, and `T1` contains `T2` among its direct or indirect interfaces. The *most specific override* is a unique override that is more specific than every other override.\r\n\r\n``` c#\r\ninterface IA\r\n{\r\n    void M() { WriteLine(\"IA.M\"); }\r\n}\r\ninterface IB : IA\r\n{\r\n    override void IA.M() { WriteLine(\"IB.M\"); }\r\n}\r\ninterface IC : IA\r\n{\r\n    override void IA.M() { WriteLine(\"IC.M\"); }\r\n}\r\ninterface ID : IB, IC { } // error: no most specific override for 'IA.M'\r\nabstract class C : IB, IC { } // error: no most specific override for 'IA.M'\r\nabstract class D : IA, IB, IC // ok\r\n{\r\n    public abstract void M();\r\n}\r\n\r\n```\r\n\r\nThe most specific override rule ensures that a conflict (i.e. an ambiguity arising from diamond inheritance) is resolved explicitly by the programmer at the point where the conflict arises.\r\n\r\nBecause we support explicit abstract overrides in interfaces, we could do so in classes as well\r\n\r\n``` c#\r\nabstract class E : IA, IB, IC // ok\r\n{\r\n    abstract void IA.M();\r\n}\r\n```\r\n\r\n> Open issue: should we support explicit interface abstract overrides in classes?\r\n\r\nIn addition, it is an error if in a class declaration the most specific override of some interface method is an an abstract override that was declared in an interface. This is an existing rule restated using the new terminology.\r\n\r\n``` c#\r\ninterface IF\r\n{\r\n    void M();\r\n}\r\nabstract class F : IF { } // error: 'F' does not implement 'IF.M'\r\n```\r\n\r\n## `static` and `private` methods\r\n\r\nBecause interfaces may now contain executable code, it is useful to abstract common code into private and static methods. We now permit these in interfaces.\r\n\r\n> Open issue: Should we support private methods? Should we support static methods?\r\n\r\n> Open issue: should we permit interface methods to be `protected` or `internal` or other access? If so, what are the semantics? Are they `virtual` by default? If so, is there a way to make them non-virtual?\r\n\r\n> Open issue: If we support static methods, should we support (static) operators?\r\n\r\n## Base interface invocations\r\n\r\nAn instance (nonstatic) method is permitted to invoke an accessible instance method override in a direct base interface nonvirtually by naming it using the syntax `Type.base.M`. This is useful when an override that is required to be provided due to diamond inheritance is resolved by delegating to one particular base implementation.\r\n\r\n``` c#\r\ninterface IA\r\n{\r\n    void M() { WriteLine(\"IA.M\"); }\r\n}\r\ninterface IB : IA\r\n{\r\n    override void IA.M() { WriteLine(\"IB.M\"); }\r\n}\r\ninterface IC : IA\r\n{\r\n    override void IA.M() { WriteLine(\"IC.M\"); }\r\n}\r\n\r\nclass D : IA, IB, IC\r\n{\r\n    void IA.M() { IB.base.M(); } \r\n}\r\n```\r\n\r\n> Open issue: what syntax should we use for base invocation? \r\n\r\n## Effect on existing programs\r\n\r\nThe rules presented here are intended to have no effect on the meaning of existing programs.\r\n\r\nExample 1:\r\n\r\n``` c#\r\ninterface IA\r\n{\r\n    void M();\r\n}\r\nclass C: IA // Error: IA.M has no concrete most specific override in C\r\n{\r\n    public static void M() { } // method unrelated to 'IA.M' because static\r\n}\r\n```\r\n\r\nExample 2:\r\n\r\n``` c#\r\ninterface IA\r\n{\r\n    void M();\r\n}\r\nclass Base: IA\r\n{\r\n    void IA.M() { }\r\n}\r\nclass Derived: Base, IA // OK, all interface members have a concrete most specific override\r\n{\r\n    private void M() { } // method unrelated to 'IA.M' because private\r\n}\r\n```\r\n\r\nThe same rules give similar results to the analogous situation involving default interface methods:\r\n\r\n``` c#\r\ninterface IA\r\n{\r\n    void M() { }\r\n}\r\nclass Derived: IA // OK, all interface members have a concrete most specific override\r\n{\r\n    private void M() { } // method unrelated to 'IA.M' because private\r\n}\r\n```\r\n\r\n> Open issue: confirm that this is an intended consequence of the specification.\r\n\r\n## Further areas to be specified\r\n\r\n- [ ] It would be useful to catalog the kinds of source and binary compatibility effects caused by adding default interface methods and overrides to existing interfaces.\r\n- [ ] We need to specify the runtime method resolution rules.\r\n\r\n/cc @dotnet/csharplangdesign @dotnet/roslyn-compiler \r\n"},"comment":{"url":"https://api.github.com/repos/dotnet/csharplang/issues/comments/287899415","html_url":"https://github.com/dotnet/csharplang/issues/288#issuecomment-287899415","issue_url":"https://api.github.com/repos/dotnet/csharplang/issues/288","id":287899415,"user":{"login":"DavidArno","id":196899,"avatar_url":"https://avatars0.githubusercontent.com/u/196899?v=3","gravatar_id":"","url":"https://api.github.com/users/DavidArno","html_url":"https://github.com/DavidArno","followers_url":"https://api.github.com/users/DavidArno/followers","following_url":"https://api.github.com/users/DavidArno/following{/other_user}","gists_url":"https://api.github.com/users/DavidArno/gists{/gist_id}","starred_url":"https://api.github.com/users/DavidArno/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/DavidArno/subscriptions","organizations_url":"https://api.github.com/users/DavidArno/orgs","repos_url":"https://api.github.com/users/DavidArno/repos","events_url":"https://api.github.com/users/DavidArno/events{/privacy}","received_events_url":"https://api.github.com/users/DavidArno/received_events","type":"User","site_admin":false},"created_at":"2017-03-20T21:11:53Z","updated_at":"2017-03-20T21:11:53Z","body":"@CyrusNajmabadi \r\n\r\n> \"Count()\" already exists...\r\n\r\nYes it does, so the genie is out of the bottle on that one. It was a mistake, but we are stuck with it. Thankfully the likes of R# warn us of possible re-enumerations of `IEnumerable<T>`, thus helping prevent its use.\r\n\r\n> No, it wouldn't force people to write anything. \r\n\r\nTrue, I could just stubbornly leave my library broken for anyone foolish enough to use `Count` on an uncountable `IEnumerable<T>`, but a desire to do things properly would force me to do something. And the only option open to me would be to pile crap upon an already broken thing by using a `NotImplementedException`.\r\n\r\n> That said, for implementations where Count really isn't something you want, you actually can now throw if you want. For example, if you want to ensure your internal collection type is never 'Count()ed', just throw in your override.\r\nThat's better than today since today you can't specialize the behavior of Enumerable.Count.\r\n\r\nNo! Forcing me to throw a crappy exception because a bad idea has just been made an official addition to an interface's contract is never going to be better.\r\n\r\nI earlier joked about how the BCL team might might misuse this feature. But if any of them were to think the way you are, it wouldn't be a joke; it would be a reality. For that reason alone, this feature needs fighting tooth and nail to protect us from potential bad decisions by well-meaning, but misguided folk. This feature could end up making even using mailman for language feature discussions seem like a genius idea.\r\n"}}
{"actor":{"display_login":"DavidArno"}}