Coder Social home page Coder Social logo

Comments (763)

DavidArno avatar DavidArno commented on May 22, 2024 40

Those "alternatives" do not address the principal motivation for the proposal, and are not, therefore, alternatives
@gafter,

In that case I'm downvoting this proposal as it:

  1. Requires CLR changes
  2. Operates via inheritance, rather than composition so cannot be applied to sealed classes and structs.
  3. It introduces multiple inheritance to the C# language.

Since the underlying aim of this proposal can be achieved using the non-alternative delegation pattern approach, which doesn't suffer from any of the above problems, this doesn't seem a good proposal.

from csharplang.

DavidArno avatar DavidArno commented on May 22, 2024 24

The spec says there are no alternatives, but there is one: the delegation pattern as suggested in Roslyn issues [Proposal] Deligation class and Proposal: C# interface delegation.

In my opinion at least, the delegation pattern has far more merit than default interface methods:

  1. It requires no CLR change (it would be a pure syntax sugar language feature),
  2. It operates via composition, rather than inheritance, allowing extension of even sealed classes and structs.
  3. It avoids introducing multiple inheritance to the C# language.

Should I submit a PR to update the spec to reflect this alternative, or will you do it, @gafter?

from csharplang.

gafter avatar gafter commented on May 22, 2024 14

@DavidArno Those "alternatives" do not address the principal motivation for the proposal, and are not, therefore, alternatives:

Virtual extension methods enable an API author to add methods to an interface in future versions without breaking source or binary compatibility with existing implementations of that interface.

from csharplang.

iam3yal avatar iam3yal commented on May 22, 2024 14

@DavidArno

It introduces multiple inheritance to the C# language.

Well, it's not like we can't mimic this today and I don't think that multiple inheritance is bad per se because as far as I understand things like the diamond problem is going to result an error but I can certainly see that it might open a can of worms. :)

I voted for this because I think it can come in handy at times but I really like to see more love to composition too.

from csharplang.

scottdorman avatar scottdorman commented on May 22, 2024 11

@DavidArno Given my earlier example of providing a default correct implementation of IDisposable.Dispose(), how would that be accomplished using your delegation pattern?

One of the goals of this proposal is:

Virtual extension methods enable an API author to add methods to an interface in future versions without breaking source or binary compatibility with existing implementations of that interface.

How would using your delegation pattern achieve that (in my earlier example) such that existing classes which already implement that member aren't broken but new classes can utilize the default implementation?

Just because a proposal requires CLR changes isn't a reason for it not to be done. If that were the case, there's a lot of language features we've gotten over the last several releases that wouldn't exist.

I don't see how this introduces multiple inheritance to the language. Yes, it provides a solution that other languages use multiple inheritance to solve, but it doesn't change the inheritance rules for C#. A class can still only inherit from a single base class and multiple interfaces.

from csharplang.

DavidArno avatar DavidArno commented on May 22, 2024 9

@eyalsk,

If multiple inheritance is seen as no worse than single inheritance, then why not just allow full-blown multiple inheritance, rather than this "by the back door" partial approach?

from csharplang.

Thaina avatar Thaina commented on May 22, 2024 6

I think abstract is outdated. Object in the real world actually isn't hierarchy. Instead it is a composite of multiple aspect. Abstract really is just a class packed along with interface that it should be separated and composed

It just extension method was introduced so late so we stuck with abstract class to contain concrete method. Abstract class that need to derived to implement NotImplementedException such as stream is one example. Some stream can seek, some can't, but it must all be stream so it need to implemented like that

Which I think it's wrong. It should be explicitly state at compile time what it could do

For the virtual method. I want to explain that most of the time virtual method has concrete logic. And request you to always include base.ThatMethod() somewhere in the overridden method
This also a disadvantage of inheritance. The implementor must know where should they put (or should not put) that base call so the logic still flowing as the api designer expect

Instead. Interface callback is more proper way to do thing like that. Because if logic really concrete and just want to allow some extension in the future. You should just made interface and extension method like a jigsaw that would be zigzag between concrete and extensible logic. So you can control where you think it should be really extended

static class Ext
{
    public static void DoSomething<T>(this T obj) where T : MyInterface
    {
         // startup
         obj.Before();

        if(obj.Predicate())
         // concrete logic a
        else // concrete logic b

         obj.After();
    }
}

public interface MyInterface
{
    void Before();
    bool Predicate{ get; }
    void After();
}

With this it more powerful to extend any class you want in any composite way you need than hierarchical that could be derived with only one linear inheritance

from csharplang.

gafter avatar gafter commented on May 22, 2024 6

Also, I would not want to rush the feature and get a suboptimal design that cannot be fixed later. I'd prefer we do it right.

from csharplang.

scottdorman avatar scottdorman commented on May 22, 2024 5

Why are we limiting to only extend by allowing private and static methods? What about protected as well?

At Summit last year, @MadsTorgersen and I discussed how this could be used in the context of helping to properly implement IDisposable. From what I can remember of the discussion, and by way of a possible actual syntax, how about something like:

public interface IDisposable
{
        bool Disposed { get; protected set; }

        void Dispose() = 
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        };

        protected virtual void Dispose(bool disposing);
}

(Putting this here so it doesn't get lost. Pending the outcome of the question asked on dotnet/roslyn#17054 (comment) I can repost the comment to the right place.)

from csharplang.

rolfbjarne avatar rolfbjarne commented on May 22, 2024 5

@DavidArno, I think my example was a bit too simplistic 😄

There are a few points here:

  • There is no such thing as extension properties, so you couldn't make my example work at all (unless C# started implementing extension properties, which of course could be done, but it would be a separate topic).
  • Extension methods break polymorphism, I can't see how you'd implement the following using extension methods:
interface I {
    public int GetSomething () { return 0; }
}
class A : I { }
class B : I {
    public int GetSomething () { return 1; }
}
class C : I {
    public int GetSomething () { return 2; }
}
class Other {
    static void PrintSomething (I obj) {
        Console.WriteLine (obj.GetSomething ());
    }
    static void Main () {
        PrintSomething (new A ()); // this should print 0
        PrintSomething (new B ()); // this should print 1
        PrintSomething (new C ()); // this should print 2
    }
}

from csharplang.

iam3yal avatar iam3yal commented on May 22, 2024 3

@DavidArno I think that you're looking at it from the wrong angle.

I don't think that the question should be whether extension methods CAN come in place of the "default interface methods" but whether they SHOULD and by that I mean sometimes there's functionality that NEEDS to be part of the interface but was missed from the design and it's too late to introduce new functionality or changes into the system so today people have two choices either to create a new interface or introduce a breaking change, both of these options might be bad, depends on your position.

Now, adding functionality through extension methods can solve the problem but sometimes it can be considered a hack to a faulty design, just as an extreme example extending a collection with a Add method through an extension method that by design needs to add stuff.

This is how I think about extension methods "if all you have is a hammer, everything looks like a nail".

from csharplang.

fschmied avatar fschmied commented on May 22, 2024 3

As in dotnet/roslyn#258 (comment), I'd once again like to challenge whether retaining binary compatibility after adding interface members should really be the "principal" motivation of this issue. (As a reaction to #52 (comment).)

To me, as an object-oriented programmer, a framework user, and a framework developer, the other motivation is at least as important, if not more so:

It also enables many programming patterns that require multiple inheritance without the issues of multiply inherited state.

Having a mixin-like mechanism, where customizable behavior can be added together with an interface contract, would be a great language feature that I would use often, in my regular every-day work. It would reduce boilerplate code, avoid awkward workarounds, and enable better designs. I tried to explain this in dotnet/roslyn#258 (comment) and in dotnet/roslyn#73.

Why's this important? Because the mixin-like mechanism does not require CLR support and might therefore have a lower bar regarding actual realization and inclusion in C# 7.1 or 8.0.

@gafter Could you comment on this?

from csharplang.

DavidArno avatar DavidArno commented on May 22, 2024 3

@CyrusNajmabadi,

That is what I thought. This feature isn't really about adding default methods to interfaces to allow for non-breaking expansion of those interfaces. It's all about adding complicated multiple-inheritance features to the language. At least this is clarified now.

from csharplang.

HaloFour avatar HaloFour commented on May 22, 2024 3

@DavidArno

"We're poking under the hood, what else can we do under here?"

Seriously, why does everything have to have an immediate and obvious use case? A lot of these minor things would be almost free if tacked onto the larger body of work. They might be worth doing, they might not. But shutting down discussion about it just because they don't fit into this incredibly narrow framework you've established doesn't sound very conducive to the discovery that the team seeks to carry out.

from csharplang.

jnm2 avatar jnm2 commented on May 22, 2024 3

I'm tired of the _interminable_ second-guessing of the team's priorities which continues to bloat many a thread. Don't incentivize them to move back to email.

from csharplang.

scottdorman avatar scottdorman commented on May 22, 2024 2

I think Proposal: C# interface delegation is useful, would reduce a lot of boiler plate code that has to be written, and would love to see it in the language; however, I don't think it addresses the ability to provide a default implementation for an interface method or property nor does it extend the interface syntax to allow private methods, static methods, "override" methods (and hopefully protected methods).

[Proposal] Deligation class looks to achieve a similar result, albeit with a much uglier and more confusing (in my opinion) syntax so it wouldn't be able to achieve the goals set out in this proposal either.

from csharplang.

Thaina avatar Thaina commented on May 22, 2024 2

This actually has alternative with generic extension method

I agree with @DavidArno

The proposal is conflict in itself. The purpose is to create function with concrete logic for interface. If it is concrete logic then it should not be overridden and we should separate some logic to be function that class derived from interface should implemented instead

from csharplang.

DavidArno avatar DavidArno commented on May 22, 2024 2

@scottdorman,

Given my earlier example of providing a default correct implementation of IDisposable.Dispose(), how would that be accomplished using your delegation pattern?

Since @gafter seems opposed to having protected members in an interface, this could be a moot point.
However, rather than duck the issue with a technicality, I should answer your question. Something like the following could be used:

// built-in types
public interface IDisposable
{
    void Dispose();
}

public class Disposable : IDisposable
{
    private readonly Func<bool> _isDisposeNeeded;
    private readonly Action _dispose;

    public Disposable(Func<bool> isDisposeNeeded, Action dispose)
    {
        _isDisposeNeeded = isDisposeNeeded;
        _dispose = dispose;
    }

    public void Dispose()
    {
        if (_isDisposeNeeded())
        {
            _dispose();
            GC.SuppressFinalize(this);
        }         
    }
}

// my type
internal class MyDisposableType : delegate Disposable
{
    private bool _disposed;

    internal MyDisposableType() : delegate(() => !_disposed, PerformDispose) {}

    private void PerformDispose()
    {
        _disposed = true;
        // etc
    }
}

How would using your delegation pattern achieve that (in my earlier example) such that existing classes which already implement that member aren't broken but new classes can utilize the default implementation?

I wouldn't as that isn't necessary. As @Thaina says, this can already be achieved with extension methods.

from csharplang.

iam3yal avatar iam3yal commented on May 22, 2024 2

@DavidArno Sure, I'd vote for it, haha... :)

I think that this solves a different problem though, even if we had MI in the language, it wouldn't solve the following problem:

Virtual extension methods enable an API author to add methods to an interface in future versions without breaking source or binary compatibility with existing implementations of that interface.

I've been asked many times before what's the difference between abstract classes and interfaces and my guess is that many people don't know the difference or they do but end up making the wrong choice and choose interfaces over abstract classes so in order to help these people a "plaster" is introduced into the language and I'm in need of one because I'm not perfect, yet. 😉

Now, I completely agree with you on the following part:

It also enables many programming patterns that require multiple inheritance without the issues of multiply inherited state.

This doesn't make a lot of sense because the next request would be to enable this for classes and maybe more specifically abstract classes, I mean why not? people are going to "abuse" interfaces just to achieve this so to me it makes more sense to add MI into the language at the same time.

It's a new feature so they can always throw an error at compile-time in cases it isn't "safe" such as in the diamond problem.

from csharplang.

rolfbjarne avatar rolfbjarne commented on May 22, 2024 2

@DavidArno

Re:

Am I missing something re,

Virtual extension methods enable an API author to add methods to an interface in future versions >>without breaking source or binary compatibility with existing implementations of that interface.
For, as in my reply to @scottdorman, extension methods surely already provide this?

You can't provide a custom implementation of an extension method.

Example:

interface IA {
    public bool Something { get { return true; } }
}
class C : IA {
    public bool Something { get { return false; } }
}

from csharplang.

iam3yal avatar iam3yal commented on May 22, 2024 2

@DavidArno

software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification

There are many interpretation to this principle, some even contradicts one another so let's stick to the wiki one: "entity can allow its behaviour to be extended without modifying its source code."

Now, you're right you can "extend" the object with new behaviour without ever touching the original class but it's important to note that you're not actually extending anything but adding new behaviour to existing objects, after all, extension methods are just static methods in disguises and we're basically passing objects to them so in some cases where things are dependent on the type it won't work, so here are two scenarios where it won't work:

  1. Let's say you're building a plugin system and you have two assemblies PluginEngine and PluginInterface, the former basically binds between the various components and creates the required objects that are used to extends the system whereas the latter is used by both the engine and plugin authors to implement the plugin.

    Let's say that you shipped a new version of the software and you want to add missing functionality to one of the interfaces in PluginInterface so you add it through extension methods but it wouldn't quite work because the engine won't pick them up so now, you need to come up with your own mechanism to workaround this limitation so you will need to modify the engine so it would understand that default methods exist in some other static classes.

    Let's say I'm a plugin author and I want to override one of the extension methods but I can't really do that so now the engine needs to be modified yet again to somehow allow plugin authors to override your default methods.

    With the default interface methods approach you will only need to update the PluginInterface and I may or may not update my plugin, ever and things will continue to work.

    Now, obviously, when the software is in development or fairly new you can change and break things with minimal costs but the moment it gained traction and you need to think about backward compatibility and still extend the software you can't introduce a breaking change.

    C# is not a new language, plugin engines were written with it and if the software has many plugins you wouldn't want to break anyone but you wouldn't want to be in a position that you can't extend the system either so this is just another approach to give you that option.

    tl;dr; may not work with reflection when a component tries to create objects solely based on interfaces and their implementations like in the case of plugin engines, IoC frameworks, etc..

  2. The using statement expects types to implement the IDisposable interface; adding a Dispose method through an extension method won't work.

Beyond these two technical points there are also authors of frameworks and libraries that want to deliver new functionality to their existing types without breaking anyone, just a matter of decision and with this proposal their consumers can override the default methods whenever they want at their own time.

then surely using extension methods to extend an existing interface is exactly what we should do, rather than being a hack?

Again, it really depends, if by design I have an interface that is missing core functionality then using extension methods to do that would be a "hack" because I can't touch the interface but if default interface methods would exist then I could add the functionality to the interface itself.

I guess that you're more pragmatic in the sense that it doesn't bother you that core functionality isn't part of the interface as long as you have access to it whereas in my mind extension methods are used to provide additional functionality on top of existing one, I'd usually put them in separate namespace and/or different assembly.

from csharplang.

gafter avatar gafter commented on May 22, 2024 2

@guylando I expect we will support protected members in interfaces. That would allow you to implement the injected property in the class (e.g. as a protected auto-property initialized in the constructor) without exposing it as part of the API.

from csharplang.

gafter avatar gafter commented on May 22, 2024 2

@gafter Those all might be true, but why is it suddenly such a high priority when it wasn't even on the radar for C# 7? Seems like extension everything is less controversial, and covers some of the use cases for this feature. I'd think you guys would explore that first and then see if this is still even worth doing.

The reason it has come to the forefront for us is a desire to use C# for interoperating with Android, where Java has default interface methods. Also, the CLR team has come to the point where they can start helping add CLR features to support language features, so that naturally helps us solve this problem at this time.

Extension everything doesn't actually address any of the motivations for this feature.

I'm confused. Those links for details on what traits are seem to suggest that traits achieve their features without using inheritance. So how does adding multiple inheritance to C# help us realise traits?

If you were to "program using traits" in C#, you'd put most of your code in interfaces, and only have tiny private classes to construct instances of them (exposed through factories). I'm not sure how prominent a style it would become, but that's the extreme end of the spectrum.

from csharplang.

DavidArno avatar DavidArno commented on May 22, 2024 2

@jnm2,

This must be a matter of opinion. To me, any injection except constructor injection is ugly.

Slight late here, but I disagree. Constructor injection is "OK, I Guess": if you have to use "objects", ie types that mix data and functionality. To me, the least ugly form of injection is injecting functions into other, higher order, functions.

(which might help explain my horror at the idea of reducing interfaces to just another base-type in an, even more complicated than present, inheritance hierarchy).

from csharplang.

DavidArno avatar DavidArno commented on May 22, 2024 2

@jnm2,

I'm tired of the interminable second-guessing of the team's priorities which continues to bloat many a thread.

I 100%. agree. Having to keep second guessing their priorities is a an absolute pain. Which is why I really wish they'd just publish a list of priorities.

from csharplang.

scottdorman avatar scottdorman commented on May 22, 2024 1

@gafter I disagree that it doesn't make sense. Take the example of IDisposable. The full pattern is to include the protected virtual void Dispose(bool disposing) method, but since it's not part of the interface (because interfaces can't have protected members) it rarely gets implemented...which means that a lot of disposable classes end up being implemented the wrong way.

a class does not inherit methods from the interfaces it implements

This is true, but isn't this proposal sort of breaking that statement? We're basically saying "Implement this interface but you don't have to provide your own implementation for this interface method because the interface is already giving you one." You can then decide later on to implement the method yourself, in which case the default interface method is effectively ignored/overridden with the actual class implementation.

We'd have to reimagine the meaning of protected in some way

How so? This is simply saying that if you implement this interface there is a protected member that must be implemented. In that respect, it doesn't seem like it's any different than saying "The syntax for an interface is extended to permit private methods (the default access is public)", which is what we have in the proposal now.

from csharplang.

iam3yal avatar iam3yal commented on May 22, 2024 1

@Thaina

I think abstract is outdated. Object in the real world actually isn't hierarchy. Instead it is a composite of multiple aspect. Abstract really is just a class packed along with interface that it should be separated and composed

Stating that abstract classes are dated is a bold statement to make.

In object-oriented hierarchies don't come from the object themselves but from the way we perceive reality and think about objects so it's really about the way we (humans) tend to organize information.

Objects aren't hierarchical but the relations between them might be for example your own family tree.

It just extension method was introduced so late so we stuck with abstract class to contain concrete method. Abstract class that need to derived to implement NotImplementedException such as stream is one example. Some stream can seek, some can't, but it must all be stream so it need to implemented like that

I think that you refer to NotSupportedException and if anything you need to blame the design and not the tool which btw given the circumstances is a reasonable design.

Extension methods wouldn't help here.

from csharplang.

Thaina avatar Thaina commented on May 22, 2024 1

@eyalsk

In object-oriented hierarchies don't come from the object themselves but from the way we perceive reality and think about objects so it's really about the way we (humans) tend to organize information.

It is just one perspective that we use to perceived a world. And which I was state, it outdated

Today there are many approach try to workaround to do composition object. Because that what we really use to construct object in factory. object is composed. Many times some object derived from something but lose some functionality. It does not hierarchy but mutation

Objects aren't hierarchical but the relations between them might be for example your own family tree.

My family tree is really great example. I may have something similar to my parents but I have many aspect lose from each of them. Object-oriented model hierarchy is wrong way to interpret real world. It can only has one parent and it can only expand

If I would represent my family tree I would use tree node, not object inheritance. I think relation is data. Not an object structure

Object that we made in programming also don't like a natural object. It mostly like artificial object that, as I said, construct from factory, which almost composed of various interface and module

if anything you need to blame the design and not the tool which btw given the circumstances is a reasonable design. Extension methods wouldn't help here.

That's why I don't said it bad. I just said it outdated. Because in the old day, that circumstance make an object-oriented hierarchy perspective most reasonable. But now we have better perspective and better tool we can revise that it have another approach which might be better

In my opinion Stream could be just interface, and might be many interface, IReadableStream, IWriteableStream, ISeekableStream. And all concrete logic of stream can ported to extension method of each respective interface. Instead of class Stream we would have IStream to wrap other object

interface IStream : IReadableStream,IWriteableStream,ISeekableStream{}

class MemoryStream : IStream { }

class HttpRequestStream : IWriteableStream { }

class HttpResponseStream : IReadableStream { }

from csharplang.

iam3yal avatar iam3yal commented on May 22, 2024 1

@Thaina

How instance method actually differ from extension method?

They differ in the rules that are applied to them for example instance methods can be virtual and as such calls to them are polymorphic whereas static methods cannot be made virtual and aren't subject to polymorphism.

Now, you copied only part of the sentence but my point above was that when things are dependent on the type then extension methods will fail to work in some circumstances because they operate on an instance of the type and aren't part of the type itself.

Just because an instance of the type is passed to an instance method at run-time doesn't mean that when you pass an object to a static method it makes it the same thing.

from csharplang.

dmitriyse avatar dmitriyse commented on May 22, 2024 1

Please consider a lightweight implementation as an intermediate stage of this feature
See discussion here #222

from csharplang.

HaloFour avatar HaloFour commented on May 22, 2024 1

@MgSam

They've been discussed since this community was on Codeplex. This is probably coming back up since we're at the beginning of a new development cycle. It's been mentioned that the inability to evolve existing contracts has been a known pain point to the BCL team. And Java has demonstrated that this mechanism works quite well.

Actually, it seems that the JVM invokevirtual bytecode will throw if there is an ambiguity. It's interesting that Java's primary motivation is the same as C#'s, but I'm curious if the C#/BCL team are as willing to allow said evolution knowing that it brings the risk of breaking existing assemblies at runtime.

from csharplang.

MgSam avatar MgSam commented on May 22, 2024 1

@gafter Those all might be true, but why is it suddenly such a high priority when it wasn't even on the radar for C# 7? Seems like extension everything is less controversial, and covers some of the use cases for this feature. I'd think you guys would explore that first and then see if this is still even worth doing.

from csharplang.

HaloFour avatar HaloFour commented on May 22, 2024 1

@DavidArno

Because there is already a huge backlog of requested features, such as records which the team have been promising since before C# 6 shipped.

There is no queue. And there have been no promises. There is a backlog, and there are championed proposals. Nothing guarantees that anything will get done, let alone what order it would happen in, regardless of any real or perceived priority.

from csharplang.

gafter avatar gafter commented on May 22, 2024 1

@jnm2 @HaloFour Once you start inheriting state, there are both difficult implementation issues and difficult semantic issues that make the language construct problematic. People generally have an intuitive opposition to "multiple inheritance", but if they delved into why, I think they would find that it is the multiple inheritance of state that is problematic. See http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf for some discussion and links to other relevant writing.

from csharplang.

gafter avatar gafter commented on May 22, 2024

@scottdorman protected would not make sense, as no class has an interface as its base class. Moreover, a method invoked from a class receiver (even the implicit this) is never an interface method (because a class does not inherit methods from the interfaces it implements). We'd have to reimagine the meaning of protected in some way to make sense of that, and I'd rather not do that before the value of doing so is clear.

from csharplang.

gulshan avatar gulshan commented on May 22, 2024

I think interfaces can just be allowed to have an unnamed nested class, that implements the interface. The nested class then can provide all the proposed features. Thus the main interfacing portion will not be polluted.

Also, how does this go with proposed extension everything proposal?

from csharplang.

DavidArno avatar DavidArno commented on May 22, 2024

@eyalsk,

Am I missing something re,

Virtual extension methods enable an API author to add methods to an interface in future versions without breaking source or binary compatibility with existing implementations of that interface.

For, as in my reply to @scottdorman, extension methods surely already provide this?

from csharplang.

iam3yal avatar iam3yal commented on May 22, 2024

@rolfbjarne Yes, well, you can "extend" the object and "pretend" that somehow you extended the type. :)

from csharplang.

DavidArno avatar DavidArno commented on May 22, 2024

@rolfbjarne,

I think you can. I haven't checked the code below, but I think it would work:

public interface IA
{
    bool Something { get; }
}

public static class IAExtentions
{
    public static bool SomethingElse(this IA a) => true; 
}

class C : IA
{
    public bool Something => false;
    public bool SomethingElse() => false;
}

The SomethineElse in C will override the extension method. Or am I wrong?

Of course, if the rules on where extension methods can be defined were relaxed, the interface could be modified to the following, to avoid creating a new type just for the extension, and without breaking existing implementations of IA (exactly as this proposal was aiming for):

public interface IA
{
    bool Something { get; }
    public static bool SomethingElse(this IA a) => true; 
}

But that may be taken the relaxation of the rules too far.

from csharplang.

DavidArno avatar DavidArno commented on May 22, 2024

@eyalsk,

I may be wandering into the realms of "devils advocate" here, but taking the definition of the open/closed principle as ""software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification" (taken from Wikipedia), then surely using extension methods to extend an existing interface is exactly what we should do, rather than being a hack?

from csharplang.

iam3yal avatar iam3yal commented on May 22, 2024

@Thaina

If I would represent my family tree I would use tree node, not object inheritance.

I didn't say you would use inheritance to implement it I just said that hierarchies exist in the relations that exist between objects but your nodes can actually be hierarchical for example FatherNode and MotherNode might have their gender fixed to male and female respectively where both of them may derive from PersonNode.

In my opinion Stream could be just interface, and might be many interface, IReadableStream, IWriteableStream, ISeekableStream. And all concrete logic of stream can ported to extension method of each respective interface. Instead of class Stream we would have IStream to wrap other object

You're right, it is an opinion and a choice but you can read about why they designed it the way they did here.

from csharplang.

Thaina avatar Thaina commented on May 22, 2024

@eyalsk

extension methods are just static methods in disguises and we're basically passing objects to them

I would like to argue that this is the underlying mechanism of class and object instance method anyway. Extension method just expose that underlying mechanism to the surface

This is the perspective that was actually blown my mind. Instance method is just actually function that always pass instance object as this. And it just that every object know it's own type. So it go referencing function from it type

How instance method actually differ from extension method?

And I think it is the core concept as golang. You can write new method for any class in golang like extension method. It very powerful and it actually work like charm

from csharplang.

Thaina avatar Thaina commented on May 22, 2024

@eyalsk And virtual method underlying is really a readonly function pointer. It just replaceable in the new class and that's all it is

Really, what I mean is mechanically it is the same. If it not virtual then it is a function. If it is virtual then it just wrap a function pointer

Being extension method mean it has no instance. It is static function in static class. And as we said from the start that what we need is "concrete logic on interface". And I think our "some circumstance" is not in this requirement

And also I wonder why logic for interface is not being concrete in your circumstance. Do you have scenario?

from csharplang.

iam3yal avatar iam3yal commented on May 22, 2024

@Thaina I don't want to derail the post too much about technicalities I feel like we're going astray and I summarized everything I had to say in my post to @DavidArno. :)

from csharplang.

Thaina avatar Thaina commented on May 22, 2024

@rolfbjarne

There is no such thing as extension properties

There is request about extension everything which was very well accepted. What left is just how syntax would be

Extension methods break polymorphism

I think it really actually honestly earnestly opposite. Extension methods on interface IS true polymorphism. You can have any concrete logic in extension method. And use interface as contract to apply to anything you like, because it is interface you can apply as many as you like to a class you made. You can derive another class to implement another interface to work with extension method for that interface

Whole System.Linq namespace is proof of the most powerful polymorphism in the whole C# library. Every kind of class implement IEnumerable<T> (and T can be any object) could use whole Linq namespace. Dictionary can iterate with select in the same manner as List without really deriving from the same superclass, just have another interface attach to it

I can't see how you'd implement the following using extension methods:

Because your sample is not really concrete logic for interface. What you actually want is, if it implement that interface, you will use its getsomething else you return 0

interface I {
    public int GetSomething();
}
class A { }
class B : I {
    public int GetSomething () { return 1; }
}
class C : I {
    public int GetSomething () { return 2; }
}

class Other {
    static void PrintSomething (object obj) {
        Console.WriteLine ((obj as I)?.GetSomething() ?? 0);
    }
    static void Main () {
        PrintSomething (new A ()); // this should print 0
        PrintSomething (new B ()); // this should print 1
        PrintSomething (new C ()); // this should print 2
    }
}

or if you want it constraint

public interface I { }
public interface ISomething : I { int GetSomething(); }

class A : I { }
class B : ISomething {
    public int GetSomething () { return 1; }
}
class C : ISomething {
    public int GetSomething () { return 2; }
}

static class Ext {
    static void PrintSomething(this I obj) {
        Console.WriteLine ((obj as ISomething)?.GetSomething() ?? 0);
    }
    static void Main () {
        new A().PrintSomething(); // this should print 0
        new B().PrintSomething(); // this should print 1
        new C().PrintSomething(); // this should print 2
    }
}

from csharplang.

DavidArno avatar DavidArno commented on May 22, 2024

@rolfbjarne,

Ah, good point. I was forgetting that:

static void PrintSomething (I obj) {
    Console.WriteLine (obj.GetSomething ());
}

is effectively just syntactic sugar for:

static void PrintSomething (I obj) {
    Console.WriteLine (SomeExtensionClass.GetSomething(obj));
}

if GetSomething is defined as an extension method.

from csharplang.

dmitriyse avatar dmitriyse commented on May 22, 2024

Making copy from #222 just to not forget about this scenario:

Default interface members can do great magic !!! with this feature we can finally get ICollection to be inherited from IReadOnlyCollection without breaking changes.
(https://github.com/dotnet/corefx/issues/16626)

public interface ICollection<T>: IReadOnlyCollection<T>
{
     // no any member should be migrated to IReadOnlyCollection.
     // So we will have ICollection<T>.Count and IRedOnlyCollection<T>.Count.
}

public interface IReadOnlyCollection<T>
{
      // The same property is defined in ICollection<T>.
      // Default impelmentation will save as from breaking changes. 
      // Existing code that explicitly implements only ICollection<T>.Count 
      int Count 
      {
            get
            { 
                   // .this is threated by the compiler as System.Object.
                   var mutableCollection = this as ICollection<T>;
                   if (mutableCollection == null)
                   {
                         throw new NotImplementedException("You should add your implementation.");
                   }
                   return mutableCollection.Count;
            }
      }
}

from csharplang.

yaakov-h avatar yaakov-h commented on May 22, 2024

Virtual extension methods enable an API author to add methods to an interface in future versions without breaking source or binary compatibility with existing implementations of that interface.

If this is indeed the goal, the current proposal sounds a lot more like hacked-up multiple inheritance than interface forwards compatibility.

Has optional interface methods a la Objective-C been considered at all? Something like this would allow for new methods to be defined, and the caller would have to check to see if it's been implemented (possibly with a != null check, or reflection) before calling an optionally-defined method.

from csharplang.

dmitriyse avatar dmitriyse commented on May 22, 2024

You can vote to reopen #222 proposal, it's clearly define the goal. #222 is subset of #52, but probably goals are totally different.

from csharplang.

yaakov-h avatar yaakov-h commented on May 22, 2024

#222 doesn't solve the case where you have a binary that implements the old interface (e.g. an extension or other loadable module) and calling code that knows about the new interface.

In #222, as far as I can tell, introducing a new method would require all code that implements it to be recompiled, which defeats the purpose of improving binary compatibility.

CLR-supported default interface methods, or optional methods, or probably a bunch of other solutions that have not been discussed - these would both address binary compatibility, not just source compatibility.

from csharplang.

dmitriyse avatar dmitriyse commented on May 22, 2024

#222 doesn't solve the case where you have a binary that implements the old interface (e.g. an extension or other loadable module) and calling code that knows about the new interface.

Exactly for this case CLR change is required. CLR should detect that old binary misses new interface members implementation and generate this implementation according to defaults of new interface.

Initially in this proposal I loose this case and conclude that we can avoid CLR changes. Now I fixed this point.

from csharplang.

dmitriyse avatar dmitriyse commented on May 22, 2024

This CLR change should not be so hard. We already have code generation for generics, Default interface members generation for old binary code can be internally reuse generic method generation.

// Old version
public interface ISomeContract
{
     int MyProperty {get;}
     string MyFormatting{get;}
}

// New version (source code)
// ----------------------------------------------
public interface ISomeContract
{
     int MyProperty {get;}
     string MyFormatting{get;}

      string GetFormattedProperty()
      {
            return string.Format(MyFormatting, MyProperty);
      }
}
// --------------------------------------------

// New C# compiler generate the next assembly for the "new version"
// --------------------------------------------------
[DefaultImplementations(typeof(__ISomeContractDefaults))]
public interface ISomeContract
{
     int MyProperty {get;}
     string MyFormatting{get;}

      string GetFormattedProperty();
}

// Internally improved C# compiler can prepare helper class in the same assembly where ISomeContracts defined.
private static class __ISomeContractDefaults
{
       public static string GetFormattedProperty<T>(T __this) where T:ISomeContract
       {
             return string.Format(__this.MyFormatting, __this.MyProperty);
       }
}
// ----------------------------------------

// Old binary
public class SomeImpl: ISomeContract
{
       public int MyProperty {get;} = 5;
       public int MyFormatting {get;} = "MyProperty={0}";
}

// Some other assembly that links old binary and new contract binary
// -------------------------------------------
// Method in new binary, that knows about ISomeContract.GetFormattedProperty.
public void TestMethod(SomeImpl c)
{
       Console.WriteLine(c.GetFormattedProperty()); // MIssed implementation, currently Runtime failure.

       // IMPROVED CLR do this under the hood instead of failure.  
       Console.WriteLine(__ISomeContractDefaults.GetFormattedProperty(c));  
}

from csharplang.

dmitriyse avatar dmitriyse commented on May 22, 2024

One additional think about this like features:
It should be implemented in every platform to which CoreFX targets (CoreCLR, .Net Framework, Mono, etc.). For example every CLR that wants to targets next platform (for example netcoreapp2.1/netstandard2.1) should implements this feature.
Only with this guarantee CoreFX team can start to adopt this feature.
So every CLR/Compiler change should have many stages:

  1. Design
  2. Implementation in all CLRs/Compilers
  3. CoreFX code evolution.

from csharplang.

gafter avatar gafter commented on May 22, 2024

Open issues are being tracked at #285

from csharplang.

gulshan avatar gulshan commented on May 22, 2024

@fschmied #164 probably enables what you are talking about.

from csharplang.

gafter avatar gafter commented on May 22, 2024

@fschmied I still think interface evolution is the principal motivation, but I'm glad the proposed mechanism also handles use cases you find more important.

from csharplang.

alrz avatar alrz commented on May 22, 2024

Does the current implementation depends on a specific clr version?

from csharplang.

guylando avatar guylando commented on May 22, 2024

How will this be used in methods which require dependency injection such as ILogger injection?

from csharplang.

gafter avatar gafter commented on May 22, 2024

@guylando My naive reaction is that I don't expect it would change how dependency injection is done. Can you please elaborate on why it might?

from csharplang.

HaloFour avatar HaloFour commented on May 22, 2024

@guylando

Where the method arguments themselves are injected? I assume that it wouldn't change anything; the implementation shouldn't know nor care where it's values are coming from.

from csharplang.

guylando avatar guylando commented on May 22, 2024

We have the following situation:
There are DAL classes per database table and they all inherit from BaseDAL and have they own interface for example IUsersDAL impemented by UsersDAL.
BaseDAL implements IBaseDAL which has all select,update,delete methods implemented in the BaseDAL and IUserDAL inherits from IBaseDAL.
There are some specific DAL classes which we now want to only allow select for them in a nice way other then overriding other methods with exception throwing.
So we refactored IBaseDAL to inherit from new interfaces ISelectable, IDeletable, IUpdatable.
And now classes can implement ISelectable instead of IBaseDAL by making IUserDAL inherit from ISelectableDAL instead of IBaseDAL.

THE PROBLEM IS that the UsersDAL class will still inherit from BaseDAL class and get all the delete, update methods. If other code will use the interface then the methods will not be visible but if some other code will use the class itself then the methods will be visible which is undesired.

So we wanted to spread BaseDAL to BaseSelectableDAL, BaseUpdatableDAL, ..etc but multiple inheritance is not allowed.

So the options we have currently are:
1)Create Base class for any combination of properties for example BaseSelectableUpdatableDAL, BaseSelectableDeletableDAL,... etc.
2)Create extension methods for the ISelectable, IUpdatable methods which will implement the select, update, methods and remove the methods declaration from the interface itself and remove inheritance from BaseDAL.

THE PROBLEM with the second solution is that the implementation requires dependency injection of the logger and other things which is not available in extension methods.

So then we found this thread about the default methods feature which can give solution to our problem by implementing logic as default methods of ISelectable, IUpdatable, etc . But this will be only possible if the default methods will have access to the dependency injection. Otherwise we are again left with similar problem to solution 2 above.

from csharplang.

HaloFour avatar HaloFour commented on May 22, 2024

@guylando

Default interface methods have no access to state* beyond the arguments of that method and this. So, no, if you must inject the logger into the implementation of that interface, default interface methods won't help you.

Update: By "state" I mean specifically the instance fields of whatever class implements that interface. Obviously you could access static fields. But considering your case involves dependency injection I doubt that's what you're referring to.

Update 2: Unless of course you expose the dependency as a readonly member of the interface, then the default interface methods could access it via this.Logger or whatever. Of course that would render the dependency public which might not be what you want. There is an open question on #288 regarding maybe allowing for other methods of varying accessibility, which might solve that problem.

from csharplang.

guylando avatar guylando commented on May 22, 2024

@HaloFour Yes, there is an ugly way around it but I am looking for a better way: creating a property in the interface and dependency injecting it in the class ctor and using this property in the default method implementation or in the extension method.
Yes if that property would not be public then it would be good.

from csharplang.

guylando avatar guylando commented on May 22, 2024

I am disappointed at c# language for failing at some basic stuff like the one talked about here, like socks proxy support for web requests, like implicit smtp support in smtp client, and other stuff I encountered.

from csharplang.

HaloFour avatar HaloFour commented on May 22, 2024

@guylando

Yes, there is an ugly way around it but I am looking for a better way: creating a property in the interface and dependency injecting it in the class ctor and using this property in the default method implementation or in the extension method.
Yes if that property would not be public then it would be good.

Inject into what, though? Interfaces can't have fields and interfaces can't have constructors. An interface can define a property as a contract but can't provide the storage. It would still be up to the implementer to actually do that.

What you're describing doesn't make sense for an interface or a trait.

I am disappointed at c# language for failing at some basic stuff like

C# is just a programming language, not a framework. Take that up with the BCL/CoreFX team.

from csharplang.

guylando avatar guylando commented on May 22, 2024

Inject into interface property, and yes I said the injection will be done in the implementing class constructor.
This is ugly and thats why i am looking for another solution for the situation I described.
I think the situation I encountered is not that special and probably many others encountered it. I wonder if there is really no nice solution to this.
There is a possibility of creating a static ioc container property somewhere and access it in the default method\extension method implementation and get logger and other dependencies from there.
But declaring static property for ioc container is anti-pattern if i remember correctly.

from csharplang.

jnm2 avatar jnm2 commented on May 22, 2024

I said the injection will be done in the implementing class constructor. This is ugly

This must be a matter of opinion. To me, any injection except constructor injection is ugly.

from csharplang.

guylando avatar guylando commented on May 22, 2024

@jnm2 constructor injection is good but this is a constructor injection into property - lets call it a property injection in order for the default method to work since it will work with the property injected

from csharplang.

GeirGrusom avatar GeirGrusom commented on May 22, 2024

Can members also be sealed?

from csharplang.

yaakov-h avatar yaakov-h commented on May 22, 2024

If the purpose is sane defaults for new functionality, why would it be sealable? It would no longer be a default interface method, just an interface method.

from csharplang.

GeirGrusom avatar GeirGrusom commented on May 22, 2024

The motivation for the proposal is not "sane defaults" but backward compatibility for interfaces.

from csharplang.

yaakov-h avatar yaakov-h commented on May 22, 2024

Well it's that you can add a member and provide a sensible default for implementations that don't provide their own - backwards compatibility concerns are the driving force here, but also they could not override it purely because they chose not to.

In either scenario, what benefit does sealing provide?

from csharplang.

GeirGrusom avatar GeirGrusom commented on May 22, 2024

It would basically do the same thing as an extension method. I'm not really arguing for sealed, more asking if it would be there.

from csharplang.

MgSam avatar MgSam commented on May 22, 2024

Reading the 2017-03-23 meeting notes, I sincerely hope you don't make the runtime behavior to choose an overload based on some deterministic behavior. I'm surprised such an option was even considered. If the programmer didn't compile it to use a given overload, this would be the worst thing you could possibly do- silently changing the behavior of the program. Much better to throw an error that will force a recompile.

from csharplang.

HaloFour avatar HaloFour commented on May 22, 2024

@MgSam

#404 (comment)

In short, if it does fail at runtime, the entire point of this feature in allowing the BCL (or anyone else) the ability to evolve interfaces by adding new default members without the fear of breaking existing assemblies goes right out the window.

from csharplang.

MgSam avatar MgSam commented on May 22, 2024

@HaloFour Unless they have a solution that will absolutely prevent this from causing brittle base case scenarios, it seems unlikely to me this feature will see the light of day.

It would also be nice if we knew what the motivation for pushing for this so hard right now is.

from csharplang.

gafter avatar gafter commented on May 22, 2024

Can members also be sealed?

Yes, and it means non-virtual. There is an open issue about whether an override can be sealed, and I expect the answer will be no.

from csharplang.

DavidArno avatar DavidArno commented on May 22, 2024

The talk of allowing protected in interfaces concerns me. Is this proposal about allowing APIs to add new methods to interfaces, or is it about supporting multiple inheritance via stateless abstract base classes?

If it is really about just adding default implementations to interfaces, and allowing the consumption of mobile APIs, why are inheritance features like protected, override and sealed even being talked about? What's the driving force behind adding these complications to this proposal?

from csharplang.

Thaina avatar Thaina commented on May 22, 2024

As our conversation progressing now I don't know what really difference between this new interface implementation and allowing multiple abstract inheritance

from csharplang.

DavidArno avatar DavidArno commented on May 22, 2024

@Thaina,

I agree. The entire discussion has become focused on allowing ever more multiple inheritance features, and the original idea of allowing default method definitions in interfaces has got lost.

from csharplang.

GeirGrusom avatar GeirGrusom commented on May 22, 2024

It greatly expands the simplicity of defining the implementation aspect of an interface, not just the public surface.

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on May 22, 2024

and the original idea of allowing default method definitions in interfaces has got lost.

The original idea hasn't been lost. The explorations currently are about what may be possible on top of that original idea.

We don't feel that these ideas conflict with each other. And, like with many efforts we've taken on over the years, we are looking to see if there are a set of features that work well together that we can take on all at the same time.

Or, in other words, we're looking to see if we should:

  1. create a smaller feature to solve one problem.
  2. create a slightly larger feature to solve many problems.

It may turn out that '2' won't work out. But we're definitely investigating it as it seems to be quite promising and addresses several areas that we've been wanting to improve for many releases.

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on May 22, 2024

If it is really about just adding default implementations to interfaces, and allowing the consumption of mobile APIs, why are inheritance features like protected, override and sealed even being talked about?

These feature sets are not in conflict. For example, i may want to provide a default implementation of some interface method. That default implementation may want to have some sort of helper method to support it (and maybe other default implementations). I may want that helper to not be part of the type, but to allow subclasses and/or subinterfaces to override how that helper method works.

That's a case where i would want a protected interface method. It would not be part of my public contract. But it would be there for the inheritance chain to use (and potentially specialize) to make the implementation potentially work better/faster/etc.

from csharplang.

DavidArno avatar DavidArno commented on May 22, 2024

@GeirGrusom,

It greatly expands the simplicity of defining the implementation aspect of an interface, not just the public surface.

Clearly it doesn't. Currently interfaces have no implementation aspect. You can't get simpler than "none".

To achieve the stated aim of adding default methods to interfaces to allow for non-breaking expansion of interfaces, then *all that is required is the ability to define a method in an interface. That's it; job done. So that's a slight reduction in simplicity, but not much. Then shoehorning full blown multiple inheritance from stateless base types goes as far from simplicity as it's possible to go.

So, all I'd really like is clarification on why all these inheritance features are needed.

from csharplang.

GeirGrusom avatar GeirGrusom commented on May 22, 2024

Clearly it doesn't. Currently interfaces have no implementation aspect. You can't get simpler than "none".

An interface currently is a type with only public abstract members. So it doesn't have "none"; it has "one".

from csharplang.

gafter avatar gafter commented on May 22, 2024

Please read the third bullet under "Motivation" in the specification:

Motivation

The principal motivations for this feature are

from csharplang.

jnm2 avatar jnm2 commented on May 22, 2024

Because API evolution, I imagine.

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on May 22, 2024

Seems like extension everything ... covers some of the use cases for this feature

Yes. But it doesn't cover all of them :) So we're exploring a direction that we think can cover them all. We feel like there is value in all of the motivations that @gafter listed. If there's value, and we can provide a suitable feature that covers all of them, then why not do that rather than coming up with a solution that then doesn't cover one (or more) of those scenarios?

from csharplang.

DavidArno avatar DavidArno commented on May 22, 2024

@HaloFour,

Seriously, why does everything have to have an immediate and obvious use case?

Because there is already a huge backlog of requested features, such as records which the team have been promising since before C# 6 shipped. So anything like this proposal ought to have to prove itself monumentally important via incredible use-cases to justify jumping the queue.

A lot of these minor things would be almost free if tacked onto the larger body of work.

This is nonsense. Every one of those "minor" things means a change to the spec, to the compiler, to tests, to the IDE, to documentation, training courses etc etc. No new feature to any language as old as C# is every going to be anywhere near free. To just implement multiple inheritance on a whim "because we were in the error" would be grossly irresponsible.

from csharplang.

DavidArno avatar DavidArno commented on May 22, 2024

@gafter,

Please read the third bullet under "Motivation" in the specification:

I'm confused. Those links for details on what traits are seem to suggest that traits achieve their features without using inheritance. So how does adding multiple inheritance to C# help us realise traits?

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on May 22, 2024

So anything like this proposal ought to have to prove itself monumentally important

We think the motivations listed here are important enough for us to warrant investigation and effort in this area. Two of the motivations have existed for us for a long time (evolution of interfaces, and more powerful compositional programming techniques). And we definitely feel now that API interoperability is very important and something that will help a lot with the many developers we have who are using C# to target platforms like iOS and Android.

These are three areas that we feel are worth it.

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on May 22, 2024

@DavidArno

I'm confused. Those links for details on what traits are seem to suggest that traits achieve their features without using inheritance.

The way we are designing default-interface-members makes them so close to traits as to be a suitable solution for that style of programming.

Indeed, if you look at the wiki article, you'll see how close they are, including:

  1. "Traits both provide a set of methods that implement behaviour to a class, and require that the class implement a set of methods that parameterize the provided behaviour.". Yup :)
  2. "An interface may define one or more behaviors via method signatures, while a trait defines behaviors via full method definitions: i.e., it includes the body of the methods." Like with our approach.
  3. "Hence an object defined as a trait is created as the composition of methods, which can be used by other classes without requiring multiple inheritance. " Like with our approach. We still do not support multiple class inheritance. We just ahve the ability to state a bunch of interfaces implemented (like stating the set of traits you take on).
  4. "In contrast, mixins include full method definitions and may also carry state through member variable, while traits usually don't." Just like us, we don't allow any state here. Hence why these are very akin to traits and not mixins.
  5. "when more than one trait to be used by a class has a method with the same name, the programmer must explicitly disambiguate which one of those methods will be used in the class; thus manually solving the diamond problem of multiple inheritance." Also what we are looking at doing.
  6. "symmetric sum: an operation that merges two disjoint traits to create a new trait". You can do this by defining a new interface that inherits two others.
  7. "override (or asymmetric sum): an operation that forms a new trait by adding methods to an existing trait, possibly overriding some of its methods". Also possible with our approach.
  8. "alias: an operation that creates a new trait by adding a new name for an existing method." Definitely possible. Define new derived interface, add new name have it forward to existing name.

The parts of traits that we seem to not really be necessarily matching are:

  1. "exclusion: an operation that forms a new trait by removing a method from an existing trait. (Combining this with the alias operation yields a shallow rename operation)."

It does not seem like this is necessarily possible with our approach.

However, to me, we're hitting so much of the core capabilities of a trait that this can suitably be a solution for bringing trait based programming to C# and .Net.

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on May 22, 2024

This is nonsense. Every one of those "minor" things means a change to the spec, to the compiler, to tests, to the IDE, to documentation, training courses etc etc.

This is actually not true in this case. As we started going down this path, we found that it was actually easier to get some of this behavior than not. i.e. many pieces "fell out" of the existing implementation. That's because we already have a large amount of infrastructure around non-abstract members. It turns out it's actually easier in some cases to just enable those in interface scenarios vs having to try to make it work in interface scenairos, but then go and explicitly disable all the things we feel shouldn't light up there.

No new feature to any language as old as C# is every going to be anywhere near free. To just implement multiple inheritance on a whim

It's not a whim. As we've stated, it's because it is an area we've been interested in for a long time. It just hasn't met the bar vs other things we've looked at when designing previous language versions. However, since we were already looking at this space for the next version of C#, it made sense to look at all the areas we might be able to enable here.

I feel like this bears repeating: If we are looking at a space, we will look at other work we are interested in that complements that space. If that work can be done in am marginal fashion, then it absolutely is something we'll consider. After all, we can get a lot more bang out for a lot less buck.

We also feel like this is a sensible approach to help us avoid designing ourselves into a corner with future work. For example, if we ignored the 'trait' space, then we might design this feature in way that worked fine for interface-evolution, but which was super unweildy in the future if we wanted to add traits.

By examining traits as we do this work, we can feel more confident that the solution we create will be suitable for all these motivating scenarios even if we may end up cutting some (or all) of them by the time the next version of C# rolls around.

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on May 22, 2024

As our conversation progressing now I don't know what really difference between this new interface implementation and allowing multiple abstract inheritance

We don't feel like we have a good solution for multiple-abstract-inheritance wrt to state. That's a problem both at the language level and the CLR level. As such, we feel like generalized-multiple-inheritance is not something we can effectively provide.

On the other hand, we do feel like we can come up with the language changes and CLR changes necessary to allow interface to have default implementations for methods. We already support mulitple-implementation of interfaces, so nothing changes there. Interfaces cannot have state, so nothing changes there. So the sorts of problems you get with generalized MI are basically mitigated. There are still some problems (like diamond member collision). But that can be addressed with required programmer disambiguation, which is something we intend to provide.

As such, this proposal gives the majority of benefits of "multiple abstract implementation" without most of the drawbacks and can suitably be implemented in an efficiently and non-costly manner for partner teams like the CLR.

Given that multiple-abstract-inheritance doesn't add anything that is necessary for our motivating scenarios, and default-interface-members does satisfy our motivating scenarios, that's why we're going with the latter.

from csharplang.

DavidArno avatar DavidArno commented on May 22, 2024

@gafter,

The reason it has come to the forefront for us is a desire to use C# for interoperating with Android, where Java has default interface methods

Is the aim to enable C#-based Android apps to consume those API's, or to allow C# to be used to create Android libraries for use by Java-based apps? Is there much call for the latter?

from csharplang.

jnm2 avatar jnm2 commented on May 22, 2024

@DavidArno

Constructor injection is "OK, I Guess": if you have to use "objects", ie types that mix data and functionality. To me, the least ugly form of injection is injecting functions into other, higher order, functions.

I somewhat agree and almost wrote about it. When using OOP, method injection is the least ugly when it's the method caller's (the client's) responsibility to control what to inject. Constructor injection is the least ugly when it's the composition root's responsibility.

When not using OOP... hmm, I've never been successful at finding a better functional replacement for OOP in C# at a nontrivial scale. I'd love to know how.

from csharplang.

Thaina avatar Thaina commented on May 22, 2024

On a second thought
I have seen some idea about, let extension method satisfied interface implementation

What if we do the other way around, by let the extension method for interface that have same parameter and method name as the interface itself contain become default interface implementation

public interface A
{
    void DoSomething();
}

public static ExtA
{
    public void DoSomething(this A a) => Console.WriteLine("TT");
}

public class T : A
{
}

And it would be transpiled into

public class T : A
{
    public void DoSomething() => ExtA.DoSomething(this);
}

from csharplang.

HaloFour avatar HaloFour commented on May 22, 2024

@Thaina

Transpiled by what? If the compiler is required to inline those extension method calls into the class during a recompilation then this misses one of the primary motivations of being able to extend interfaces without requiring recompilation of existing assemblies.

from csharplang.

HaloFour avatar HaloFour commented on May 22, 2024

@gafter @CyrusNajmabadi

I wonder how far of a jump it would be from traits to mixins if you exploit this possibility of an interface defining protected members. The "state" could be just a protected property exposed on the interface with which the default members can interact. The compiler could provide a mechanism to auto-implement said properties explicitly in order to create/manage that state.

Or if "extension everything" ever considers tackling extension fields then mixins could share that same mechanism in default property members or default "extension" fields.

from csharplang.

jnm2 avatar jnm2 commented on May 22, 2024

@HaloFour I was wondering the same thing. They have already decided to tackle diamond method inheritance. Armed with this experience, how intractable would state actually be?

from csharplang.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.