Skip to content
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

Define a default delegate type of Action/Func from method group or lambda #3990

Closed
GSPP opened this issue Jul 17, 2015 · 10 comments
Closed

Define a default delegate type of Action/Func from method group or lambda #3990

GSPP opened this issue Jul 17, 2015 · 10 comments

Comments

@GSPP
Copy link

GSPP commented Jul 17, 2015

The Action/Func delegate types have largely replaced custom delegates from the .NET 1.0 days. Action/Func should be considered the default delegate types if applicable.

C# should be able to implicitly convert method groups and lambda expressions to Delegate type. Also, method groups and lambda expressions should be converted to Action/Func whenever a concrete delegate type could not be derived otherwise.

Motivating examples:

  • Control.Invoke(() => txtName.Text = "x");
  • var setter = () => txtName.Text = "x";
  • condition ? (() => 1) : (() => 2)
  • new [] { () => Step1(), () => Step2() }
  • new [] { Step1, Step2 } /*method groups*/

None of these work as of C# 5. I think the language could be changed so that these example work naturally. The result should be that the Action/Func types be used implicitly.

There are cases where Action/Func do not apply. out and ref come to mind, so does variance. In those cases an error message should be emitted. Those cases are rare and we are not worse off than before.

Often, this will be ambiguous between Func<...> and Expression<Func<...>>. Since expression tree usage is much rarer and tends to be more "expert-level" the default should be Func<...>.

Related: https://github.com/dotnet/roslyn/issues/3661

@dsaf
Copy link

dsaf commented Jul 20, 2015

Subsumes / relates to the last three examples: #1419

@gafter gafter changed the title Define an implicit conversion from method group or lambda to Action/Func Define adefault delegate type of Action/Func from method group or lambda Jul 24, 2015
@gafter gafter changed the title Define adefault delegate type of Action/Func from method group or lambda Define a default delegate type of Action/Func from method group or lambda Jul 24, 2015
@HaloFour
Copy link

Something like this has come up a couple of times in other proposals, although I guess not on its own. I'm all for it, as well as var for lambda expressions. Is it worth the cost of an extra delegate invocation per implicit conversion?

I must say that while I do make use of Func and Action frequently I still prefer defining proper delegate types as a part of a public contract especially where the arguments of those delegates might not be immediately obvious. Having argument names is self-documenting as opposed to arg1, arg2, etc.

@GSPP
Copy link
Author

GSPP commented Aug 16, 2015

If there is a performance issue associated with this kind of conversion we could add CLR support for fast conversion (maybe a helper method or an intrinsic helper method). The compiler could convert manually on downlevel runtimes. On newer runtimes the fast path would be used with no semantic change.

@gafter
Copy link
Member

gafter commented Nov 20, 2015

Unfortunately this doesn't work if any parameters are ref or out.

@paulomorgado
Copy link

@HaloFour,

With var you can't tell a delegate apart from an expression tree. Translating var to Func<>/Action<> and a set of Expression.Create<>(<delegate>) methods can overcome this.

I wonder if the proposed solution to name tuples for tooling would be enough for naming arguments of exposed Func<>/Action<> "values".

@alrz
Copy link
Member

alrz commented Dec 14, 2015

@gafter Can you please elaborate, why structurally equivalent delegates would be a breaking change? Because if it wasn't the case, auto generated delegates and function types would solve the issue with ref and out parameters, right?

@gafter
Copy link
Member

gafter commented Dec 14, 2015

@alrz Whenever you add conversions to the language for types that already exist, you break some cases of overload resolution that can appear in existing code.

"Auto-generation" of types doesn't work very well if those types can appear in public APIs. We would need a mechanism to unify them across assemblies, but the CLR does not currently provide such a mechanism.

@GSPP
Copy link
Author

GSPP commented Dec 14, 2015

Maybe just not do any of this for out/ref delegates? They are extremely uncommon.

@bbarry
Copy link

bbarry commented Jan 21, 2017

Playing around with my local clone and tryroslyn and reading #7278 (specifically the bit about the parenthesized lambda expression spec deviation), I started thinking about CS0815 which led me to this issue.

I think implicit conversion to a delegate is the wrong way to go (personally I'd prefer implicit expressions if I were to choose one over the other; that said I think I'd much rather continuing CS0815 over either choice).

I would propose instead of implicit conversions, making use of the delegate keyword:

  • Control.Invoke(() => txtName.Text = "x"); This is valid syntax already given that the invoke method here is void Invoke(Action a)
  • var setter = () => txtName.Text = "x"; could be written
    var setter = (delegate)(() => txtName.Text = "x"); (currently error CS1514: { expected) or
    delegate setter = () => txtName.Text = "x"; (currently error CS1002: ; expected and error CS0103: The name 'setter' does not exist in the current context) in both cases the type would be Action
  • condition ? (delegate)(() => 1) : () => 2 here the type would be Func<int>

not sure about these ones though:

  • new [] { () => Step1(), () => Step2() } a delegate "cast" specifier could work if both were the same type
  • new [] { Step1, Step2 } /*method groups*/ punting for now..

tryroslyn

@CyrusNajmabadi
Copy link
Member

This has actually been done. Lambdas now automatically have a natural type of Action/Func if not converted to something else.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants