I'll start out by saying that I may be completely missing the point. So feel free to leave your comments. I'm looking for any help I can get at this point.
Code for this article (and further experimentation) is available on GitHub: jeremybytes/interfaces-in-csharp-8. Note: at the time of this writing, the code uses .NET Core 3.0 Preview 8.
Some of the new features include the following:
- Default implementation for interface members
- Access modifiers - public, private, protected, internal
- abstract members
- static members
Default Implementation of Interface Members
Default implementation has been the most widely-touted enhancement to interfaces in C# 8. Mads Torgersen wrote an article on the topic: https://devblogs.microsoft.com/dotnet/default-implementations-in-interfaces/. This article has the example that I've seen repeated when this topic is brought up.
The assumption is that we have an interface with a single member, and a class that implements that interface.
Then another member is added to the interface. To keep the existing implementations from breaking, we add default behavior to the new interface member.
Here is an ILogger interface that has 2 members. The second has a default implementation (this is in the ILogger.cs file in the GitHub project):
The original class only implements the first member (this is in the InitialLogger.cs file in the GitHub project):
Since the 2nd member (the Log method that takes an exception parameter) has a default implementation, this code builds just fine.
We can put together a little console application that shows that both methods work (this is in the Program.cs file in the GitHub project);
The output shows that calls to both of the Log methods work:
Of course, we are also free to provide an implementation of the 2nd member in the class. This would hide the default implementation.
My Thoughts
When I first heard of this feature, I thought it was a bad idea. This blurs the line between an abstract class (that contains implementation) and an interface (no implementation).
My recommendation would be to avoid default implementation for a few reasons.
First, I have only seen trivial examples where a good default exists. Most of our code is more complicated than that. If I want to add "Save" functionality to my API, there is probably no good default for that. If I end up throwing a NotImplementedException as a default, then that doesn't accomplish anything other than moving compile-time errors to run-time errors.
Second, we already have a way to extend functionality of an interface -- through interface inheritance. By creating a new "extended" interface that inherits from the original, the existing interface implementations continue to work. And if a client needs the extended functionality, then the client can use the new extended interface (this also helps us follow the interface segregation principle).
Third, interfaces are a difficult enough concept as it is. When I started programming professionally, it took me a good 2 years before I really understood interfaces and what they were good for. This was such a frustration to me that I've spent the last 7-1/2 years helping developers understand interfaces (this includes 33 live presentations, various blog articles, and a Pluralsight course that has over 100,000 unique viewers). If nothing else, this points out that the topic can be a challenge for developers to grasp.
Messaging from Microsoft
Most of the messaging from Microsoft has been around this -- adding functionality to an existing API. The idea that if you have an existing interface, you can add to it without breaking the initial implementations.
But it turns out that this is not the only reason this feature was added to the language.
For this, we can turn to C# 8 Language Design Proposals. Here is the article for default implementation: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/default-interface-methods.
There are 3 reasons stated for this change: (1) extending APIs, (2) interoperability with Android (Java) and iOS (Swift), (3) supporting the "traits" language feature.
"Traits" are really a way of doing mix-ins with classes. The idea is that small groupings of code can be combined to form classes.
I think that traits can be a good idea; however, I think that using interfaces to implement that feature is a mistake. Another construct in C# would be less confusing to developers (even if it compiles down to the same IL).
To show the confusion that's on its way, let's take a look at some of the other changes to interfaces that facilitate these features.
Access Modifiers
Previously, access modifiers were not allowed on interface members. Here's an example from a .NET Framework 4.7.2 project:
Previously, all interface members were automatically visible. We weren't even allowed to mark something as "public". If we think of interfaces as a contract, then it makes sense that we are not allowed to use access modifiers. The terms of the contract need to be visible to everyone -- both the clients and the implementing classes.
But things are different with C# 8. We can have access modifiers. Here's an example of an interface that has a "protected" member (this is in the IReader.cs file in the GitHub project):
This causes a bit of confusion when implementing the interface. Mainly because the tooling isn't there yet. (I'm hoping it will be there soon; .NET Core 3.0 releases in 3 weeks.)
If we create a new class and mark it with the interface, we get the standard errors that there are members that are not implemented:
Note: since the Save method has a default implementation, it does not show up with the missing members.
I use the Ctrl+. shortcut to implement interfaces (or use the light bulb helper):
This, however, only implements the non-protected member. So if we use this "fix" and try to build, we still get errors:
The protected member is still not implemented.
To implement the protected member, we need to do an explicit implementation. This associates the method explicitly with the interface. This is another option on the Ctrl+. menu:
But if we do this, it implements *both* of the methods explicitly:
Note: Regardless of whether we use "Implement interface" or "Implement interface explicitly". The Save method (which has a default implementation) is not included. If we want to override that behavior, we need to type that in ourselves.
Now we have 2 implementations for "GetItem": one associated with the class, and one associated with the interface. This is probably not what we want.
You can take a look at the IntReader.cs file in the GitHub project to see the implementation that is probably more inline with what we want in this case:
This is a tooling problem. It can be fixed in Visual Studio, but it opens the door to some of the other things that come up when we have access modifiers on interface members.
Private Members
Since we can add access modifiers to interface members, we can have "private" members.
[Update Note: This example is quite embarrassing since I added a "Save" method to an "IReader" interface. You shouldn't do this. At the time, I was experimenting with syntax and wasn't thinking about the interface itself. Then this article got a bit more popular than I was expecting. More well thought out articles are available here: A Closer Look at C# 8 Interfaces.]
In this code, the "Save" method is marked as "private". The compiler will only let you mark a member as "private" if it has an implementation. So that's good.
But what would we do with a private interface member? The answer is not much. It is not accessible outside of the interface, so it is really only good if we want to create a method that will be used in other default implementations in the interface.
At this point, we're really creating a class, and an abstract class would be a much better solution.
Abstract Members
The thing that make me go O_o is the fact that we can have "abstract" interface members. The following code is valid and builds (this is the final IReader.cs file from GitHub):
In this code, the "GetItem" method is marked as "abstract".
What does this even mean? When we're dealing with abstract classes, abstract members have a declaration but not implementation. That describes *everything* in an interface (okay, it described everything in an interface until we had default implementation).
I'm a bit confused by this. The code behaves the same way whether the "abstract" is here or not.
I'll need to do a bit more research to find out why this is necessary.
Static Members
Interfaces can also have static members. This is something that is brand new. Statics were not allowed at all in interfaces previously.
If you're curious about using static members in interfaces, take a look at the tutorial: Update interfaces with default interface members in C# 8.0. This example shows how static members can be used to parameterize default implementations.
My Thoughts
Static members break the spirit of interfaces. Interfaces have to do with describing a contract -- something that a client can rely upon being there and an implementer has to include. The tutorial shows using static fields to hold data. Those are now implementation details.
My Thoughts
In my view, interfaces in C# 8 are a bit of a mess. We can no longer think of them as a contract. They are no longer the "shape" of a set of members in a class or struct. Instead, they can contain implementation. The members are no longer automatically visible, they can be private, protected, or internal.
I may be completely missing the point. But I can no longer answer the question: "What is an interface?"
An interface is no longer one thing. It has multiple aspects depending on how we want to use it. It will take a while to make sense of the new world. If you want to share your views, please leave a comment. I'm looking for clarification and to gain a better understanding of the changes.
Update: More thoughts here (a bit more organized, too) - A Closer Look at C# 8 Interfaces.
Stay tuned to the GitHub project (jeremybytes/interfaces-in-csharp-8). There will be more samples added and more articles. Together we can figure this out.
Happy Coding!
I am C# developer for almost 15 years and recently I started coding in Scala. It's turned out that all latest features that were standard become in Scala become a new feature in C#. Scala has traits, this is a very powerful feature and I believe it's a way to slightly move your focus into functional programming view. This is not a fully correct statement, but design in Scala using traits is very different, not difficult, but different. Once you start learning Scala and the new design approach, you will see how it can be used in C# as well.
ReplyDeleteSo, I think what's happening right now is shifting the whole methodology from OOP to functional way and this is make sense.
Don't be frastrated, it's very normal to reject or deny things at the beggining. Try to start learning Scala or F# and you will see thing differently and you will see a new way of design and coding. And the most important: you will understand why they doing this
I disagree. I'm very familiar with F#, but what's been done with interfaces is, in my opinion, unnecessary.
DeleteC# < 8.0 - want a contract? Interface! Need shared implementation details? Abstract base class!
I have yet to see an instance of the new interface methodology that wouldn't have been served as well or better (since the interface would remain the contract!) By an abstract class, implementing an interface.
I strongly suggest this takes a previously clear delineation, and just muddies the water.
You can't inherit from (abstract or otherwise) class multiple times, though.
DeleteThese new interfacess look like a backdoor attempt to introduce multiple inheritance into C#, without having to deal with most of the associated complications, since fields are not allowed in interfaces, and therefore cannot be multiply-inherited.
With fields, you would have to decide whether to duplicate or merge them (a-la C++ virtual inheritance) whenever there is a diamond-shaped dependency in the inheritance graph. Also, their offsets would shift whenever the class is not the first element in the inheritance list, requiring adjustment to method implementations. And probably a myriad other things I'm not aware of because I don't actually design compilers for a living...
@Branko I agree, thanks for great perspective.
DeleteI think this feature of interface will provide flexibility when dealing with same contract with multiple class's. I am still seeing interfaces are contract in nature, and we have to think those as contract only while designing architecture, later with complexity of the requirement and design slandered perspective, interface will not create stuck situation and we can easily deal with those scenarios with grater complexity.
DeleteAs a (very) old C++ developer, I'd have to agree with Branko. In early C++, abstract classes were used much the same way as interfaces are now but they only worked i) because C++ supported multiple inheritance and ii) the implementation class could in itself be subclassed as necessary. Very powerful, but too confusing for some I guess. This C# approach brings back some of the power of multiple inheritance, but while it avoids some of the less savoury side effects, it appears to add a whole lot more (just different ones). Rather preferred the C++ approach myself...
DeleteInstead of actually make the language more malleable and usable functionally, with these stupid new syntactic sugars, they have given the appearance to mostly novices that the language is comparable to c++/scala
DeleteHi I totally with Doward. Interfaces have a purpose distinct from abstract classes. Mixing both seems an unnecessary addition.
DeleteI tried to post but it botched up. Suffice to say I 95% agree with you, and I'm not a newcomer
ReplyDeleteJava has this feature for years, it's named "Default Methods".
ReplyDeleteI learn C# since its very first version, for 17 years now. Consider myself an expert (for living I run services in the cloud, not compilers though) and totally agree it's a mess. The feature I wish did never come to the light, hardly can name anything as bad as this one.
ReplyDeleteI hear you, these new features can be quite a challenge to understand. But in my view they are all fully justifiable if not a godsend...
ReplyDeleteFirst off, it's important to understand nothing is lost in terms of interface abstraction. Basically interfaces remain purely abstract because they have no state; you can't add instance fields to an interface.
Default
As a seasoned Java dev I can vouch for default interface methods, you will come to appreciate them, I promise. I understand your point of view, however, regarding "why not use an abstract class instead of an interface default methods?" I'll attempt an explanation.
Default methods target different use-cases. One big one is "interface evolution" -- the ability of an interface to change without breaking existing usages. Essentially default methods allow you to add a new method to an interface without breaking existing implementations since existing implementations fall back on the defaults.
Other use-cases for default methods target the design of brand new interfaces. For instance, its convenient to place default (or "standard") implementation detail directly in the interface in terms of other interface methods and properties. Think Name an Title properties where the default implementation for Title returns Name. There are tons of those in real life. So in a way default methods contribute *more* abstraction to your architecture by allowing you to use interfaces where you otherwise would use abstract classes or other means.
Indeed since C#'s type system is single *class* inheritance based, we are limited to interfaces for multiple inheritance, which is 99.9% beneficial! But with default interface methods we have a ton more capability. Traits will make this more obvious.
Access Modifiers
Now that we have default methods we have implementation detail and with implementation detail we need "information hiding." For instance, if we have a large default method implementation we probably want to break it up into smaller units of execution e.g., private methods, but we don't want the methods to be exposed because they are purely implementation detail, hence the need for `private` and other modifiers.
Static Members
Generally you want members declared closest to where they are used and/or scoped. Supporting static members in interfaces is another step toward achieving that goal. Think of a Color interface with an internal ColorImpl class. As a convenience the interface provides static Color properties for common colors WHITE, BLACK, RED, etc. And also provides static methods for obtaining a custom Color e.g., rgb(). Better these members on Color than in some other class, no?
Abstract
There's nothing special about this in terms of an interface, it only exists to be explicit about a member to distinguish it from default, static, or other implementation (private etc.)
That's it in a nutshell. Hope I've provided some clarity here.
@Scott
DeleteI'm not a brand new dev, but I haven't used abstract classes for anything special beyond providing default implementations.
With interfaces providing a default implementation, I can't think of any reason to use an abstract class anymore.
Do you still see a place for abstract classes, or is this a first step towards deprecation?
The default method of interface is "borrowed" from Java 8, Java brought it up as a remedy for lacking extension support. Is it necessary to C# or just because "me too"? Interesting.
ReplyDeleteI agree.
ReplyDeleteDefault implementation does muddy the water.
Interfaces in C# were always looked at as definition of behavior and shape and a way to achieve multiple inheritence.
It is now being shifted from being just a "definition".
I am not fully convinced that we needed default behaviour within an interface at all.
They could have gone with another "Type" of maybe "TraitInterface" or "ExtendedInterface" to make it clearer.
Otherwise the obvious question would be, why didn't they just change the way abstract classes work in C# to support multiple inheritence?
This seems more gimmicky or more to appeal to functional programming geeks rather than evolving the language.
The same problems and concers were raised when Java was getting default methods in interfaces. I specifically raised eyebrows at "another way of doing multi-class inheritance" as it seemed.
ReplyDeleteBut no worries. The world doesn't end with this feature, and in fact it is used only in specific cases. That is libraries that needs to be extended but still be compatible with existing code base. Other than that people tend to ignore this feature, because they don't see anything super-useful in it.
Thanks for bringing this to general attention :)
Whether in my own programming or in third party libraries, I'll see if the new features are practical or annoying.
ReplyDeleteBut the debate focuses on whether the language team would have to introduce new elements or redefine old ones to implement multiple inheritance, mixings, traits, etc.
It is true that for many novices it is difficult to understand the concept of interface and in which cases it should be used. But once we master the concept, these new 8.0 features are very easy to understand.
So, if the new features of the elements serve to implement new features such as multiple inheritance, mixing, and so on. Maybe it's better to introduce them as a simple addition to something you already understand (even though you had a hard time understanding it), than as something completely new that you have to understand from the beginning.
With time we'll see that it's better but I'm glad that, in one way or another, the language team continues to modernize our working tool.
Thanks
I can't edit my comment.
DeleteBut in my final sentence I meant that it is difficult to evaluate whether it is better to redefine old elements or create everything from scratch to implement new concepts.
Santiago
I can sympathize with you in regards to teaching others about interfaces and how this changes much of what I previously preached. That being said, I am looking forward to seeing more support for multiple inheritance. We preach interfaces over base class but in reality I see falling back to abstract classes for default behavior far too often. Since interface default methods are stateless, I can see some good things that can come from this, but I agree that it feels like a mess. Hopefully it works out well.
ReplyDeleteThis feature is extremely useful, if not a godsend. As an example, if LINQ was implemented with default interface methods instead of extension methods then it would have been much better. Take the Reverse() method...I may have a collection that can more efficiently implement the ability to iterate in reverse, but I can't replace the current behavior of enumerating the entire collection into a temp list and then reversing it. This is a very significant problem for large collections, like a BTree collection I use for database storage which may contain gigabytes of file system backed data. If Reverse() was an optional interface method I could just provide my own optimized Reverse() implementation.
ReplyDeleteInterface Default implementations are just an alternate syntax for extension methods. Consider all the ext methods defined in Enumerable (All, Any, Select, Where, etc). Would you want all of them defined in an abstract base class (which you'd need any class you where you use them to derive from)?
ReplyDeleteThe default method implementations seem like a poorly thought-out replacement for extension methods. Extension methods are, in my mind, superior because they can be separately namespaced (and therefore, included or not with 'using' statements) and they already support access modifiers public and internal. It's very common in my code to have an IMyContract interface and then to have a static MyContractExtensions class for extension methods (usually trivial combinations and overloads of public methods for improved readability). This strategy helps to keep the interface slim, but also allows it to be expressive and flexible. Access modifiers on interfaces are an absolute FAIL in my book. The point of an interface is to tell the outside world what a class *does*, but these protected and private access modifers instead tell *how* the work must be done internally. That's an awful situation and I'll be highly recommending my teams not to use this feature. Static interface methods actually might be an interesting idea. Consider something like a factory method which may return any of a number of concrete implementations depending on context. However, that is problematic because it's not extensible by classes downstream. Maybe it's a way to better organize those static utility methods which every project seems to accumulate over time? I would love to have mix-ins or some other mechanism to streamline delegation, but abusing interfaces this way doesn't seem to be it.
ReplyDeleteI completely disagree with your statement about default method implementations being a poor replacement for extension methods. Extension methods are the worst feature ever added to C# in my opinion. They obfuscate your code and the meaning of your code changes drastically based on the using statements at the top of your file with very little hints in the actual lines you are writing as to what is providing the functionality you just called. And the geniuses who wrote ASP.NET core took it a step further and have the extension methods included in classes in the default namespace of the class you are working with but coming from an assembly added for those features. So now knowing what your code does depends on what assemblies are referenced from your .csproj file and the prayer that the naming of the extension method can lead you to that source code if your IDE can't. It is worse than going back to C code with no namespaces, since C would at least have the include file you could reason from.
DeleteDefault methods on the other hand provide two essential values on first glance. The first is if you are creating an interface for the first time and know that things should almost always be done a certain way for 95% of the places that are implementing your interface, you can avoid that duplicate code in 95% of your code. The second is if you are extending an interface in a future version of your API and you either don't want to or don't have access to every class that implements your interface, then you still have a path forward if the default behavior meets the case the majority of the time.
I assume that the private and protected access modifiers are expected to be used as part of these default implementations to separate what is to be called by a public consumer of the interface and what is to be used internally without exposing things you would like to keep unavailable to the public. Another use might be part of a deprecation strategy where a default implementation of a desired new outward definition calls a newly "private" interface. But that is just a guess. This article is my first introduction to those features.
Overall these look like good enhancements to extend DRY coding principles, minimize the public API exposed by your classes, and have more effective control over what is required for a class implementing an interface.
@Whiteknight. It's easy to conflate the two, but default interface methods and extension methods are two very different features, each designed with different goals.
DeleteDefault methods are designed primarily for interface evolution. They facilitate adding new methods to existing interfaces without breaking the interface contract. The important difference here from extension methods is that interface implementations can implement the new methods, which is otherwise impossible with extension methods since they dispatch statically -- extension methods are not part of the interface contract.
Extension methods provide the means to add new methods to existing types, including classes you otherwise can't change. Of course extension methods are not directly added to the types, but are instead added as special type information for types so you can use them as if they were. Under the hood extension methods generated as static methods completely disconnected from the extending types. Thus you can't implement or override them as you can with default methods.
So there you have it. Default methods are more for adding methods to existing interfaces you own so you don't break existing implementations while allowing new implementations to override the default code. While extension methods are typically used for adding methods to existing classes you otherwise can't modify, an excellent alternative to the conventional "Util" or "Helper" classes.
Cannot extension methods in c# to provide same functionality as default methods in Java?
ReplyDeleteDefault implementation is a great replacement for extension methods, especially for something like LINQ, now we will be able to override extension methods for our custom IEnumerables, so for example .Count() can access the Length property directly instead of enumerating the whole collection. That's a good thing.
ReplyDeleteI'm pretty sure this feature will be abused but so is every other.
Access modifiers is a great thing too, I needed recently to have an interface with public members available from the outside of my class, at the same time I wanted to pass some hidden property associated with objects for internal use in my library. There is currently no good way of doing this without violating the LSP
the only thing that will help is the live sacrifice of a C# programmer, hopefully sooner rather than later
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteDefault implementations sound like they could help alleviate the code bloat of generating interfaces just for testability.
ReplyDeleteAs it is in C# < 8, if you want to do unit testing, you have to have an IMyService for every MyService, even if you'll only ever have one implementation. It would be great to see that go away.
Like you, I too struggled to understand Interfaces as contracts. In my mind, Interfaces should control how a class is IMPLEMENTED (as in, an interface for - creating a class). The unfortunate reality is; however, Interfaces exist to control how a class us USED (as in, an interface for - using a class). This discrepancy is frustrating. While these changes to the C# language seem unnecessary (blurring the line between what is an abstract class and what is an interface), they do make - some - sense.
ReplyDeleteHumans, all too often, embrace the illusion of black and white. Because we like to see fine lines, we resort to drawing our own where they dont (or shouldn't need to) exist. Interfaces tend to confuse people, it seems, because they are not necessarily objects, or blueprints, or doctrine. They are a message to the compiler: "This method exists; that's all I know about it". The rules of many a programming language only require that, for each method, an implementation exists; not that the implementation makes sense. Because interfaces are purely abstract, their members are no more meaningful than "foo" and "bar".
Ask yourself: how useful is a contract if 'existance' is it's only condition?
The new features in C#8 could provide alternative conditions. Now, black and white are not our only options. We can have a contract, and enforce it too.
This comment has been removed by the author.
ReplyDeleteI do not fully understand why private, protected and abstract.
ReplyDeleteHowever, if you think as the interface as a contract between types, in particular for the context of a generic type constrained to the interface, then static members make sense. I would be interested in static fields, mind you, but static methods. In particular static method that operates of values of the types... yeah, I want operators.
Perhaps thinking of the interface as a contract between types instead of a contract between objects makes sense. Perhaps that inspired protected and abstract (I'll go read on github after posting here). However, we do not inherit from a generic, not that I know, at least. As in, you are not going to swap the base type at runtime... then why do you need protected or abstract in an interface?
I think private is there to add helpers to enable the default interface implementation. No practical value for the consumer.
After some reading, I think I get it, abstract and protected are among interfaces. That is, an interface can have a protected member that is accesible to other interfaces that inherit from it. Similarly with abstract.
DeletePersonally, I think they should roll out without that, at least for the initial release of this feature.
I've been with Microsoft since VC++ 1.0, when it shipped in a box, with a T-Shit, and the original blue infinity symbol. And I've been with C# since its inception in 1999, and I'm very glad to hear of this addition.
ReplyDeleteIt has always been sad to me, that people who have had this hatred for the so called DIAMOND-MENACE inheritance, had to GIMP the language from the start, without at least alotting "choice" to those who wanted multiple-inheritance.
Even with this change, I would never recommend anyone using interfaces with C#. It is a gigantic waste of time just to cut/copy/paste the same interface prototype over and over again, let alone, when it is suddenly 6 months down the road, and something new comes along and people have to revist old code, to shove in more cut/copying/pasting, to make ends meet.
I won't even go into the pains of interfaces at debug and run-time, but I will say, even with the DIAMOND-MENACE, anyone with any version of Visual Studio have always had the abilty to EASILY identify the actual declared type of a variable that is supposedly suffering from the DIAMOND-MENACE, as well as use run-time .NET reflection, to identify the delcared type programtically.
At any rate.
My suspician is, someone at Microsoft finally retired but is still lingering with some control over things.
I can say this, cause if the person(s) were truly gone, then we'd just point blank get multiple inheritance.
And to further why I can sqay this, is that Microsoft was alwayss flexible and pursued back-wards compatability, especially in the later 1990's and early 2000's. The only exception I was ever aware of that contgradicted Microsoft's strong and openly reknown desire to maintain backwards compatability was C#'s interfaces-vs-multiple-inheritance, and C#'s garbage-collection-vs-run-time-scope-calling. These two aspects we adamately NOT ALLOWED, and I know, cause I tried way back then to get Microsoft to at least allow a compiler option.
At any rate, more options is always good, and only mal-contents dreading the DIAMOND-MENACE would otherwise spew discontent on this parade.
At least C# now has a little bit of multiple inheritance feature. Maybe it would be better to make a separate entity for that. Stateless class, or something like that. Because only thing, that cannot be contained inside interfaces right now are data members.
ReplyDeleteWe wanted Traits. Instead we got a Frankenstein's monster between abstract class and interfaces.
ReplyDeleteI agree with pretty much all in this article and I'm open to be wrong, but it really feels like the wrong solution to the problem. Create a new concept instead of muddying the waters of what we already have.
I'm not too interested in multiple inheritance although I'm sure there are clever usecases for that I just don't realize yet, but I'd love to see shapes/traits similar to what we have in TypeScript.
Default Implementation of Interface Members is a bad idea. Even back in the COM days, it was understood that interfaces would not be modified. The method in the interface could be deprecated, but not added to. If you want to add method, then create a new interface that inherits from the original interface. Its just wrong.
ReplyDeleteI think this post is missing the primary use case, and the one that it was truly intended for, which is maintaining compatibility. Once an interface is out in the world, currently it is nearly impossible to extend or add to without it being a breaking change (such as changing an interface to an abstract class). Breaking changes are not always feasible, or worth the cost. Extension methods can solve this in a small number of cases, but don't really fully solve the problem. This solution allows for library authors, (especially of core pieces like logging which end up with many implementations) to extend their functionality without doing breaking changes all the time. I would not expect application authors to take advantage of this usually.
ReplyDeletehow on earth does the defalut implementation work with things like.. properties? constructor injector? What is the context of this in a defalut implementation?
ReplyDeleteI'm so confused
I'm a PHP developer (including other languages) for many years, I started learning C# about 3 days ago because I feel that PHP has many missing features in it's OOP implementation which makes DDD a bit messy some times.
ReplyDeleteI was looking on C# for a while now and I didn't know that C# does not support traits, which is unfortunate, and then I stumbled upon the interface default methods new feature as of C# and I got really confused.
I wasn't confused about the language or how to use the new feature, but on what they where thinking :/ I agree with you, an interface has to have one purpose, it is a contract, nothing more, nothing less.
I do like to have the ability to define the access level of methods inside an interface for numerous reasons, but defining the body of a method inside an interface, that feels very alien and wrong to me.
I would prefer if they introduce a trait or mixin keyword instead of just adding that functionality into interfaces, it looks like laziness to me.
Currently, in C# v8 we can do the following:
https://dotnetfiddle.net/7i2WKE
This not only looks confusing, but it also introduces another problem (at least for me since I'm very new to C#).
The first issue is confusion, we use interface for implementation and for a contract now? Very against SOLID principles.
The second problem is that in order for me to access the default implemented methods of an interface I have to define the interface type when creating a variable, for instance, IHello hi = new Hello();, then I can access the methods of IHello interface, but doing so I'm unable to access any methods I might have in Hello class, for instance, the following code in C# 8 throws exception:
https://dotnetfiddle.net/eaKGt4
Program.cs(26, 30): [CS1061] 'IHello' does not contain a definition for 'SaySomethingElse' and no accessible extension method 'SaySomethingElse' accepting a first argument of type 'IHello' could be found (are you missing a using directive or an assembly reference?)
and if I switch from IHello hi = new Hello(); to Hello hi = new Hello();
then I'm getting the following exception:
Program.cs(24, 30): [CS1061] 'Hello' does not contain a definition for 'Greet' and no accessible extension method 'Greet' accepting a first argument of type 'Hello' could be found (are you missing a using directive or an assembly reference?)
two times, one for each call of Greet method.
I would prefer to have something like this:
https://dotnetfiddle.net/Y46qC7
it would be more clear and would work better in my opinion.
I think C# has one drawback that this new features solve and that is having a way of a class inherit implementation form multiple interfaces. In C# < 8 you could have implementation only in a class or abstract class and a callas could only inherit from one class. This was a limitation in C# and these new futures seems to solve that problem.
ReplyDeleteYeah like many have said above, what we all wanted was Traits (like Scala traits, which have no analog in F# or Java).
ReplyDeleteAs a 15+ year C# developer (with 10 years Scala experience), I can say with precision that having the ability to componentize
implementation into various types (like I can with Scala traits) is _very_ useful for composition.
I also agree it would have been better to just "give us mixins" as a new / separate type, instead of trying to implement this into the existing interface machinery. Specifically because it will be / is confusing to people who are use to interfaces, but
not use to mixins (or hybrid mixin/interfaces, called traits). Scala does a lot of things differently than C# which allows traits to exist as first class citizens, which would require _a lot_ of changes to interfaces to accommodate.
What we want is an "interface like thing", that allows auto properties (but not fields), no constructors, and all the normal method/property access levels and modifiers that exist in C#. But - _also_ has an implicit (or explicit) `this` reference, and fully participates in class method implementation (i.e. adding a mixin which has a concrete method "public void Func()" to a class with a method "public abstract void Func()" will fulfill that method contract).
Although this change with Default Interface Implementation _does not_ permit full-featured mixins, so I'd be in favor of them just backing it out (leave interfaces as they were) and just add a new mixin (or a hybrid mixin/interface) type.
Late to the conversation here.. I tend to agree with most about the purpose of interfaces to act as a contract.. that said, suppose we are really following the dependency inversion principle... DIP says that we should depend on abstractions and not on concrete implementations, right? In C# if I build my interface and I cannot add operators to it, I am unable to do certain things without breaking DIP. Only concrete classes can have operators because they are public and static. It's not possible to define an operator outside of the concrete class prior to C#8.. because static and abstract were not allowed in interfaces.
ReplyDeleteThe ability to define operators on interfaces (and really any method type) allows us to keep the code SOLID, default implementations and access modifiers just add to that flexibility to allow more control over how methods get implemented (does it really make sense to implement an abstract interface method in each class.. or is it better to implement it once at the interface?)
Maybe I lost the thread of the article... if anyone knows of a way to provide operators to interfaces before C# 8, please let me know.
I guess reusing existing keywords for all the new features is done to not break any existing code. Every new feature has to be expressed with the existing limited set of keywords .. thus traits get implemented (not yet completely) via "interface" ...
ReplyDeleteI wish they would keep the Interfaces the way they used to be - no implementation allowed. With the world getting more and more permissive, I think we should discipline ourselves to not go overboard. I personally would not put implementation in Interfaces because I do not believe it is a good coding practice. Just because I could does not mean I should. Yes I could drink a bottle of Vodka, but should I?
ReplyDelete