Proposal: Allow calling interface methods through 'this' #2881
Replies: 28 comments
-
Not being able to call interface members through using System;
public interface IHelloWorld
{
void HelloWorld() => Console.WriteLine("Hello, world");
}
public struct HelloWorldStruct : IHelloWorld
{
public void DoHelloWorldBoxing()
{
((IHelloWorld)this).HelloWorld();
}
public void DoHelloWorldNonBoxing()
{
CallHelloWorld(ref this);
static void CallHelloWorld<T>(ref T @this)
where T : struct, IHelloWorld
{
@this.HelloWorld();
}
}
} But it'd be a lot nicer to be able to just call it directly. |
Beta Was this translation helpful? Give feedback.
-
The team already decided on the syntax |
Beta Was this translation helpful? Give feedback.
-
@HaloFour doesn't that only point to implementations from |
Beta Was this translation helpful? Give feedback.
-
Unfortunately that's the case with default implementations regardless of how you call them. The runtime still boxes the receiver and passes as an instance of the interface. |
Beta Was this translation helpful? Give feedback.
-
I don't believe so, reading #2337 it sounds like that syntax would also be used to invoke the default implementation on the interface directly: #2337 (comment) |
Beta Was this translation helpful? Give feedback.
-
I mean if we have something like this: interface I { void M() {} }
class B : I { void I.M() {} }
partial class C : B, I { void I.M() {} }
partial class C { void Test() { base(I).M(); } } the last line calls to which implementation? what if we want the impl from |
Beta Was this translation helpful? Give feedback.
-
IIRC that calls the default implementation defined on
I don't think that the Either way I don't think changing the behavior to surface implemented interfaces via |
Beta Was this translation helpful? Give feedback.
-
Couldn't the compiler generate a private method for the explicit interface method to call so that any local calls can directly use the private method? That could even allow other access modifiers. I would love to be able to write
What if you just give explicit implementations lower priority for overload resolution than everything else? Potentially even lower than extension methods if necessary. |
Beta Was this translation helpful? Give feedback.
-
Yes. The C# compiler goes out of its way to make the method uncallable. That was an explicit design decision. You can write your own private method and have the explicit implementation call that. .NET doesn't care what the accessibility level or the name is of the overriding method, as long as the signature matches. That's why you can do this in VB.NET: Public Class Foo : Implements IDisposable
Private Sub Close() Implements IDisposable.Dispose
End Sub
End Class |
Beta Was this translation helpful? Give feedback.
-
But why? It doesn't even give a warning for giving a regular method the same name as an explicit one so it obviously can't be that bad of a design decision, yet it "goes out of its way" in a way that actually costs runtime performance to prevent a slightly less verbose implementation of the exact same behaviour. |
Beta Was this translation helpful? Give feedback.
-
Because the regular method doesn't have the same name.
How often do you really want to call an explicit implementation from within the class? The language shouldn't optimize for the unlikely. |
Beta Was this translation helpful? Give feedback.
-
If the C# compiler used a callable name (sometimes referred to as a "speakable" name) for an explict interface implementation, there's a significant probability that it would collide with a method name already in use. Making the method uncallable (by giving it an "unspeakable" name) prevents these collisions. |
Beta Was this translation helpful? Give feedback.
-
It might not compile to the same name, but it sure does have the same name in the source code. My point is that if that was actually a bad pattern worth preventing, then there should be a warning for it.
From a But from a |
Beta Was this translation helpful? Give feedback.
-
No it doesn't. The name in source code includes the interface name, just as it does in IL. Having another method with the same name isn't considered a bad pattern. It's fairly normal, especially where the names collide between two interfaces and you need to explicitly implement one or the other, like with |
Beta Was this translation helpful? Give feedback.
-
I agree, but I got the impression you felt differently because you said:
If it's not bad, then why explicitly go out of your way to prevent a simpler implementation? |
Beta Was this translation helpful? Give feedback.
-
Making the explicit implementation uncallable enables implementing two interfaces that share a method that differs only by return type. |
Beta Was this translation helpful? Give feedback.
-
I think this proposal is valid as long as this is not proposing a breaking change. Also, I have found the concept of "Most specific override rule" here- https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/default-interface-methods#the-most-specific-override-rule . If I am not wrong, according to this rule, even with default implementations, there will always be exactly one "most specific" implementation (either default implementation from interface or an implementation in the implementing class) for any interface member. And this proposal is about calling that single most specific implementation of some interface member through Please correct me if I'm wrong. |
Beta Was this translation helpful? Give feedback.
-
If the mere possibility of ambiguity was enough to prevent a feature from being implemented, then we wouldn't have method overloads at all. I can't help but feel that you're being deliberately obtuse given how obvious the solution is: allow it if there is no ambiguity or give an error if there is any, just like with other method overloads. |
Beta Was this translation helpful? Give feedback.
-
If C# were being designed today you'd probably have a number of proponents of that theory. Method overloading represents probably the most complicated part of the C# compiler.
That would immediately break a lot of existing code. Given how I wasn't there 20 years ago when this was being designed. I doubt the current team members have the context that went into the decision. The CLR provides a fairly flexible method for overriding members (both for interfaces and base classes), C# chose one design and VB.NET chose a different design. Neither design prevents developers from accomplishing anything. C#'s syntax optimizes for the common case whereas VB.NET's flexibility comes with a lot of verbosity in all cases. I'm not opposed to having syntax for invoking the most specific override of a given interface member, I just don't think that those members should appear to be automatically "inherited" into the current class, either publicly or via |
Beta Was this translation helpful? Give feedback.
-
I wonder if one could convince the language team to consider |
Beta Was this translation helpful? Give feedback.
-
I've got some context here (though i was an intern at the time :)). What is it you'd like to know? I think the issue here has gone over the topic space pretty well afaict. |
Beta Was this translation helpful? Give feedback.
-
I'd be curious as to the design process that led to explicit implementation of interface members. It's not something born of the legacy of C++ or Java. It's quite a different from the approach taken by the VB.NET team, which wasn't inherited from VB either. Given the flexibility provided by the CLR it's interesting to hear why the languages chose to surface that functionality differently. |
Beta Was this translation helpful? Give feedback.
-
my recllection is that these methods were truly thought of us bridges from the interface into functionality provided by the class. it was not felt that they were something expected to be exported from teh class itself as part of its surface. After all, ignoring renaming, there would be no way to export multiple interface methods with the same sig (which was hte prime case for explicit interface impl). So the general feeling at the time was:
The idea of having the class itself want to defer to the interface didn't every really seem important. why would it need to since it had full access to its full surface area? I can't say as to why VB did what it did. My guess is that they just found it easier or more cohesive to just have the methods be normal speakable methods and not have to have the indirection of the explicit one calling the actual class method. So they merged it into a single concept where you could directly provide the redirected name. |
Beta Was this translation helpful? Give feedback.
-
Another way to think about it: explicit interface impl was expected to be the rare case. i.e. you'd normally just implement the methods implicitly and be good and done with things. C# even goes extra far to make that easy. for example: class C
{
public void Foo() { }
}
interface IFooable { void Foo(); }
class D : C, IFooable { } In this case, C# will ensure that instances of D will see So, in essence C# heavily weights toward making implicit impl easy and the norm. And explicit-impl exists primarily for collisions (i.e. times when you can't be implicit). VB heavily weights toward making everything explicit. Tihs has led to where we are now where calling through the interface method was never felt to be something that important. But dim changes that equation (significantly) IMO. |
Beta Was this translation helpful? Give feedback.
-
I can see 3 general possibilities: 1
2
3
So where's the use case that you think would cause a breaking change? |
Beta Was this translation helpful? Give feedback.
-
Mind expanding on this? I didn't see DIM as being much more than an addendum to the contract between the interface and the consumer of that interface. The implementer isn't really involved unless it implements those defaulted members. In that case it makes sense to want to invoke the default implementation, like a override of a virtual member might want to call the base member. But beyond that I don't see it that necessary to invoke the default implementations (or the most specific override). DIM doesn't feel like traits or a way to compose common behavior to types given that the defaulted members are not "inherited" by the implementer, which, IMO, is slightly unfortunate. |
Beta Was this translation helpful? Give feedback.
-
DIM provides actual functionality that a class may want to use. Very specifically, it is different from before since previously the class would only have been able to use functionality that it itself created (and thus would have access to). In the non-DIM world, i see little need for a class to call through the interface methods of itself (instead it could just call into the real code that those interface methods just ended up using). But now, the "real code" lives in the interface portion itself. In this regard DIM interfaces straddle the line between being an interface for a class that they implement, and being an abstract base class that they inherit. In the latter view, you would def expect to be able to easily get at this functionality as if it was just in a subclass. Note: i don't feel strongly about this. :) |
Beta Was this translation helpful? Give feedback.
-
Instead of restricting it to only work with Let
The run-time behaviour should be identical to the trick to access interface member with generics:
For example, interface I { void Foo() { } }
struct Struct : I, IDisposable
{
void IDisposable.Dispose() { }
// No boxing.
public void Close() { this.(IDisposable).Dispose(); }
// Boxing.
public void Bar() { this.(I).Foo(); }
}
// No boxing.
new Struct().(IDisposable).Dispose();
// Boxing.
new Struct().(I).Foo(); Given the new syntax,
Did you mean "generate a private method for the default interface member implementation"? This will not help in avoiding boxing, because all the compiler can do is to call the default implementation in the private method, and it's the action of calling the default implementation that causes boxing. (More generally, calling a method not implemented by the value type itself requires boxing. So the DIMI is like The compiler cannot copy the code of the default implementation, since the interface might be in another assembly and should be able to be replaced without having to recompile the |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
In C#, interface methods have to be called through the interface. Previously without default implementations of methods, classes implementing an interface can also call them directly (if not explicitly implemented). Also extension methods to some interface (not implementation class) was/is available to
this
of the implementation. But now interface methods with default implementations have to be called through the interface, which sometime become very inconvenient. So, proposing here to allow calling interface methods throughthis
(implicit direct call or explicit call throughthis.
). The following snippet is to show the proposal with example code-Beta Was this translation helpful? Give feedback.
All reactions