-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Suggestion: Action method chaining for expression bodies #16961
Comments
Do you have a specific type in mind for which this would be useful? Is it really that common that you want to have a method that just calls methods on one object and does not do anything else (no assignments, no conditions, etc.)? |
Javascript has no such language feature enabling this. jQuery explicitly wrote its functions to behave fluently by returning the appropriate instance. You can do the same in C# today. |
This would be a nice feature to have. Though i'm worried a bit about this potentially confusing users. There have been numerous APIs i've worked with that force 'statement' chaining because the types are not fluent themselves. Because it was never legal to call anything off of a void-returning call before, this would not be a break, and would be easy to add into the language in a legal manner. Personally, I'd be for it. But i'd need to get a sense of what the rest of the LDM thinks. As time has moved on, we've gotten more and more love around making more things possible with expressions, and not requiring so many statements all the time. This would fit into that bucket. |
@HaloFour I think the idea here would be that this would work even for APIs that didn't take advantage of fluent patterns. I know i've run into this with APIs over the years, and it's been pretty annoying. |
I wouldn't like |
Interesting. This would result in source breaking changes that wouldn't have been breaking before: changing the return type from void to a non- |
I figured as such, but as jquery was given as an example I thought it important to mention that jquery accomplished through careful use of the existing language features, not through special fluent language features. As for treating other libraries as fluent, there are a couple of existing proposals to try to tackle this. The proposal above is similar to method cascading, although without the special operator. However, tangentially, in a number of ways C# isn't very friendly for writing fluent APIs. A lot of APIs rely on property setters and outside of initializers they don't play very well with fluent APIs. LINQ query expressions also hit an awkward wall once you need an operation that doesn't match a LINQ clause. |
Not deeply familiar with how the C# compiler would handle it but it seems it'd be more efficient to internally just keep a reference to the instance the methods are on than to actually pass back an object reference for each call. Instead of a new operator it might make more sense to add a keyword or add a different void-like return type but I think it simpler to just use void. I don't think it'd break any existing code and it'd allow existing code to be used this way without rewrite. I think jQuery itself is a good reference as to when this form of coding makes sense. LINQ is somewhat similar. As said the style has little to do with JavaScript itself and more to do with being a useful pattern. In some cases it can make code much easier to read and it seems to fit in with the on-going push for using expressions to keep code terse. It doesn't seem nearly as strange to me as having assignments as expressions anyway. |
I believe it would be a breaking change. Currently this code compiles as the lambda parameter 'x' couldn't be of type 'A' exactly because you can't call methods on void. If you enable this the code will become ambiguous. using System;
class C
{
static void Main()
{
M(x => x.M1().M2());
}
static void M(Action<A> a) {}
static void M(Action<B> a) {}
}
class A
{
public void M1() {}
public void M2() {}
}
class B
{
public B M1() => this;
public void M2() {}
} |
I'm pretty sure |
@TessenR Good point. But, in general, we've never taken the "if could cause an overload resolution in error in lambdas with overloads" to justify not doing something. The reason for this is that it prevents us from ever taken something illegal and making it legal. We think that's too onerous, and too unlikely, to warrant having it interfere with language progress. |
I skimmed through the OP and got to the line: void MyMethod (Object obj) => obj.A().B().C(); and thougth, "the language already supports that", before realising that I like @YaakovDavis's suggestion of a brand new syntax for this proposal though as it avoids that confusion. |
I personally don't like this. But it mirrors what we see nearly all of the time when we change the language. Any time we add something new, we often get a huge amount of pushback asking for some new piece of feedback to make it clear what was going on, and to help avoid the language being "deeply confusing". However, as people become comfortable with a language feature then they start feeling the opposite. The don't want hte differences called out. And they want things to just feel the same as the current language. A great example of that was extension methods. People were literally flipping out (negatively) when we added them. After all "obj.Foo()" was now a static call to Foo with 'obj' as the first argument. To some people, this language change was so awful and so unacceptable, that they believed they could not use C# again, and that the community would never accept this in the simple So, while i agree that this would be a 'change' from the language as is. I don't see why it necessarily needs to be confusing. Once people learn, then this will just be C#, and it can be totally fine to be reusing the bog standard syntax that people are used to in a fashion that can make total sense in any reasonable coding context. |
If you could do the following, then the dot syntax makes sense:
Otherwise, the dot operator loses its consistency. Also, |
The After all, I'm not so sure one of the design principles of C# is to compensate for the discrepancy between how an API is designed and how a consumer would like to use it. |
I disagree. For example, we added object-initializers so that people could work with APIs where you had to create an object, and then use a bunch of statements to initialize it. A lot of our language design is precisely around making existing APIs be much nicer to work with in the language. |
A csharplang issue was created to continue this discussion: dotnet/csharplang#781 |
It'd be useful if action methods could be chained jQuery-style so they could be used as an expression. If the compiler understood that any method that returns void should carry on the original instance reference to all following methods in the line the entire chain could be an expression.
With an obj like:
void A () => Do something..
void B () => Do something else..
void C () => Do this too..
So instead of:
void MyMethod ( Object obj ) {
obj.A ();
obj.B ();
obj.C ();
}
We could just do:
void MyMethod ( Object obj ) => obj.A ().B ().C ();
The text was updated successfully, but these errors were encountered: