Replies: 27 comments
-
Why? What's the point of accepting an interface when any caller with a method can just use method group conversion (or a lambda, if they don't know about method group conversions) to get a delegate? Also, should lambdas be somehow convertible to interfaces, to make code like this work? public void MethodWithCallback(ICallbackInterface callback) { … }
…
MethodWithCallback(() => DoSomething()); Or would you have to write the following? MethodWithCallback(new CallbackInterfaceDelegate(() => DoSomething()));
Are you talking about something like the following? (Where using (new DisposableAction(() => DoSomething()))
{
…
} How is that better than what you can do today? using (Disposable.Create(() => DoSomething()))
{
…
} I'm not against this proposal, but the justification presented here sounds weak to me. The one in https://github.com/dotnet/coreclr/issues/6934 (avoiding allocations) makes more sense to me. |
Beta Was this translation helpful? Give feedback.
-
Avoiding allocations is definitely a valid use case as well. In the coreclr example, since Delegate would just implement the interface, it seems like you would just get lambda to interface for free. |
Beta Was this translation helpful? Give feedback.
-
Looks like everyone has one of these 😆
|
Beta Was this translation helpful? Give feedback.
-
The desire for this feature has crossed my mind many times, and yet I'm stuck on two things:
|
Beta Was this translation helpful? Give feedback.
-
If you look at https://github.com/dotnet/coreclr/issues/6934, The Delegate class can just directly implement the interface. Therefore, there would be no need for any CLR support. The lambda support is a little harder, but just making delegates implement an interface would be fairly simple. I think something like a lambda would need to be implicitly convertable, but I also think that would require CLR support. |
Beta Was this translation helpful? Give feedback.
-
Oh, a custom class deriving from Delegate. So we will be emitting hidden adapter classes. |
Beta Was this translation helpful? Give feedback.
-
@jnm2 we already do, if my understanding of delegates and IL is correct. We'd just need to make that |
Beta Was this translation helpful? Give feedback.
-
Yes. Another example would be IProgress. |
Beta Was this translation helpful? Give feedback.
-
Hey, so if you have a logger interface and all the methods have default implementations except one, the delegate would still be able to implement the interface just fine. 😆 |
Beta Was this translation helpful? Give feedback.
-
So is this proposal about explicitly writing a delegate that implements an interface, or is it about having the compiler emit a delegate that implements the interface in order to support lambda syntax et al with such interfaces?
That's how functional interfaces work in Java. You can use lambda syntax as long as they have exactly one required method. But they can have any number of default methods. |
Beta Was this translation helpful? Give feedback.
-
This proposal doesn't actually propose anything, but the original coreclr issue seems to be the former, explicitly writing a delegate that implements an interface. The latter would be neat, too. |
Beta Was this translation helpful? Give feedback.
-
For the case of using a lambda expression to provide an interface implementation, the delegate itself is kinda superfluous. Why not just implement the interface on a/the closure object? Though, personally, I would rather extend the anonymous class syntax.
Also, if something like this got in, the first thing I would do is write an analyzer rule to forbid it's use in a using clause and direct people towards a try-finally clause instead. |
Beta Was this translation helpful? Give feedback.
-
It would be better to support true local interface implementations, as it's supported in Java. public interface ISomething
{
void Do(int arg);
void Do2(int arg1, int arg2);
}
void SomeMethod()
{
ISomething a;
Func<int, ISomething> factory;
int result = 0;
// "Anonymous" interface implementation
// Same syntax as for anonymous types, but as it's assigned to interface variable,
// compiler "know" what methods anonymous type should implement
a = new {
void Do(int arg) {
// you shouldn't declare method as public
result = arg; // result variable is captured as closure!
}
void Do2(int arg1, int arg2) {
result = arg1 + arg2; // result variable is captured as closure!
}
};
// Factory function. Anonymous interface implementation can be declared as lambda-function
factory = (x) => new {
void Do(int arg) {
result = arg + x; // x lambda argument is presented as hidden field in anonymous class
}
void Do2(int arg1, int arg2) {
result = arg1 + arg2 + x; // x lambda argument is presented as hidden field in anonymous class
}
};
a = x(5);
// "Local" interface implementation inside method code
class LocalImplementation : ISomething
{
void Do(int arg) {
result = arg; // result variable is captured as closure!
}
void Do2(int arg1, int arg2) {
result = arg1 + arg2; // result variable is captured as closure!
}
}
a = new LocalImplementation();
} |
Beta Was this translation helpful? Give feedback.
-
That would be a separate proposal. The LDM has already weighed in on that idea on the Roslyn repo, though: dotnet/roslyn#13 |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
I gotta agree with @Opiumtm on this. I'd much rather see #738 (dotnet/roslyn#13) than this if I had to pick. |
Beta Was this translation helpful? Give feedback.
-
I think allowing delegates to implement single method interfaces would be mainly useful for readability. In fact, many developers choose to use For example, I see a lot of code where constructors take a |
Beta Was this translation helpful? Give feedback.
-
How about the other way around? interface IInvokable { void Invoke(); }
IInvokable Method() => () => {}; |
Beta Was this translation helpful? Give feedback.
-
@nehmebilal If those people don't create a custom delegate type now, what makes you think they would create a custom interface type if they could use a lambda to implement it? @alrz How is that different from a delegate? |
Beta Was this translation helpful? Give feedback.
-
@svick This feature is supported in Java and frequently used. It's also often used in public Apis where a single method interface is a parameter that can be replaced with a lambda expression. That said, I agree that the use of custom delegates seems like a reasonable alternative. Another similar feature that is popular in Java and lacking in C# is inline implementation of an interface (very commonly used in Android apps). |
Beta Was this translation helpful? Give feedback.
-
Already suggested at #738. |
Beta Was this translation helpful? Give feedback.
-
Java chose "functional interfaces" as the mechanism through which lambdas work and there is no other way to use them. So it's not like Java elected to support these interfaces on top of something more akin to function pointers or that they've become a common convention despite other ways to accomplish the same goal; they are the only way to do functional programming in Java. Sometimes there are APIs in .NET where a feature like this would be useful. For example, when a method accepts an |
Beta Was this translation helpful? Give feedback.
-
I'd like to see something akin to #738 but using it to implement single method interfaces seems like it would become an poor and unfortunately common practice. Needing to pass an |
Beta Was this translation helpful? Give feedback.
-
@aluanhaddad It should be as simple as IComparer myComparer = (x, y) => {// implementation} Why would that be a poor practice?
Programming languages drive APIs and successful APIs make languages successful. |
Beta Was this translation helpful? Give feedback.
-
@nehmebilal it should be as simple as Comparison<T> myComparison = (x, y) => // implementation. |
Beta Was this translation helpful? Give feedback.
-
Only in the sense that anytime an API exposes one it should also expose overloads that accepts the other. But many APIs don't do this which puts the onus of wrapping the lambda on the consumer of that API. And it is kind of silly because both |
Beta Was this translation helpful? Give feedback.
-
Evaluating that code it looks like delegates could support having method members and implementing interfaces, while having access to delegate internals like the call list, the method signature that it accepts, etc. The above code could be written as something like: private delegate void OnDisposeAction() : IDisposable
{
private bool disposed = false;
// Maybe Lazy is overkill
private Lazy<Action?> action = new(GetAction);
private Action? Action => action.Value;
public void Dispose()
{
bool wasDisposed = Interlocked.Exchange(ref disposed, true);
if (wasDisposed)
return;
action?.Invoke();
}
private Action? GetAction() => GetInvocationList().FirstOrDefault();
} It wouldn't guarantee immutability like the custom class that is implemented, and that area would also need some attention, but it's closer to what the delegate's purpose is. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I noticed that https://github.com/dotnet/coreclr/issues/6934 exists, but there is nothing in this repo about that. I could definitely see this being a cool and useful feature. It looks like it would even be supported today by the current clr.
Currently, I have a good amount of libraries where I provide both an interface and
Action
for running callbacks. This means I have to have 2 entry points for each type of callback. If a delegate could implement the interface, I would only need a single entry point.It could also enable some interesting uses, especially around IDisposable, as you could create in theory a semi-inline disposable without the need to build a special class to do so.
Beta Was this translation helpful? Give feedback.
All reactions