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

Declaration of ref/out parameters in lambdas without typename #338

Open
gafter opened this issue Mar 24, 2017 · 17 comments
Open

Declaration of ref/out parameters in lambdas without typename #338

gafter opened this issue Mar 24, 2017 · 17 comments
Assignees
Labels
Feature Request Needs Implementation The specification for this issue has been approved, it needs an implementation Proposal champion
Milestone

Comments

@gafter
Copy link
Member

gafter commented Mar 24, 2017

@ViIvanov commented on Sat Feb 07 2015

Hello!

I have a little suggestion for C# language.
Let us have delegate like this:

delegate T Parse<T>(string text);

and we want to create an instance:

Parse<int> parse = text => Int32.Parse(text);

All is OK. What about ref/out parameters in a delegate?

delegate bool TryParse<T>(string text, out T result);

when we want to create an instance…

TryParse<int> parse1 = (string text, out int result) => Int32.TryParse(text, out result);

…we shoud to specify types on parameters.

Why is this required? What about a syntax below:

TryParse<int> parse2 = (text, out result) => Int32.TryParse(text, out result);

?


@mikedn commented on Sat Feb 07 2015

In this particular case you don't need to write any of the parameter stuff, it's just:

TryParse<int> parse1 = Int32.TryParse;

@alanfo commented on Sun Feb 08 2015

This would be a worthwhile improvement to type inference, in my view.

As ref and out are full keywords, they can't possibly be type names and so there appears no reason why the compiler won't be able to infer the type from the delegate signature.


@paulomorgado commented on Sun Feb 08 2015

I'm not sure I understand what you are proposing.

Would you care to elaborate a bit more?


@ViIvanov commented on Mon Feb 09 2015

@paulomorgado Of course! Let us see a code below:

delegate T Parse<T>(string text);
delegate bool TryParse<T>(string text, out T result);

static void Main() {
  // We can create an instance of Parse<int> like this:
  Parse<int> parseHex1 = (string text) => Int32.Parse(text, NumberStyles.HexNumber);
  // or like this (and, I think, this way is more simple and more readable):
  Parse<int> parseHex2 = text => Int32.Parse(text, NumberStyles.HexNumber);

  // To create an instance of TryParse<int> delegate
  // we must explicitly specify a types of arguments in a lambda expression:
  TryParse<int> tryParseHex1 = (string text, out int result) => Int32.TryParse(text, NumberStyles.HexNumber, null, out result);

  // And we can not now use a sintax like this:
  TryParse<int> tryParseHex2 = (text, out result) => Int32.TryParse(text, NumberStyles.HexNumber, null, out result);
}

Why this is meaningful:

  1. In some scenarios user can have a delegates with a few (three, four, …etc) parameters and when at least one of them has a ref or out modifier user must explicitly specify types of all "delegate parameters".
  2. Follows from previous - we can not use anonymous types as type-parameters in delegates with ref or out parameters.

@omariom commented on Mon Feb 09 2015

👍


@paulomorgado commented on Mon Feb 09 2015

I ink you are missing the fact that, although you can't declare by reference type parameters in C#, when a parameter is expressed as of being of type ref T (or out T, which is the same for the CLR - just extra validation from the compiler), the type is, actually, &T, which is not the same as T.


@ViIvanov commented on Mon Feb 09 2015

@paulomorgado Excuse me, can you explain what do you mean? Why in you point of view

TryParse<int> tryParseHex1 = (string text, out int result) => Int32.TryParse(text, NumberStyles.HexNumber, null, out result);

is correct (it is valid C# code) and

TryParse<int> tryParseHex2 = (text, out result) => Int32.TryParse(text, NumberStyles.HexNumber, null, out result);

is not? In a both examples, types of arguments exactly the same. But, in the second line, it calculated by the compiler, not specified by user explicitly.


@paulomorgado commented on Mon Feb 09 2015

I was trying to understand your issue, and I think I got it: the compiler should be able to infer the types from usage when there are out parameters. Is that it?


@ViIvanov commented on Mon Feb 09 2015

@paulomorgado Exactly! I'm sorry for my bad and poor English.


@alrz commented on Thu Nov 19 2015

👍


@Thaina commented on Thu Jan 14 2016

+1

thanks


@asvishnyakov commented on Fri Jan 15 2016

👍

LDM Discussion

https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-02-21.md#declaration-of-refout-parameters-in-lambdas-without-typename

@Rabadash8820
Copy link

+1 Just ran into this issue myself! Like all type inference this would definitely make for some cleaner code!

@ThadHouse
Copy link

With the addition of in this should be something looked at even more. I can see in delegates being common for performance heavy code, and this could be really helpful.

@paulomorgado
Copy link

@ThadHouse, what do you mean by in delegates?

@ThadHouse
Copy link

public delegate void InAction<T>(in T value);

A delegate with some of the parameters being in parameters.

@HaloFour
Copy link
Contributor

HaloFour commented Jun 1, 2018

@ThadHouse

I can see in delegates being common for performance heavy code,

Really? I'd think that value copying would be peanuts compared to the allocation and delegate dispatch.

@paulomorgado
Copy link

Oh! Delegates with in parameters.

@ThadHouse
Copy link

@HaloFour from testing, the dispatch has gotten much better in recent .NET Core versions, and if static delegates ever get added they'll be even better. And the allocation can be made to only happen once at the beginning of a program, so that can be less of a concern done right.

@alrz
Copy link
Contributor

alrz commented Jun 1, 2018

compared to the allocation and delegate dispatch.

static delegates (#302) would help with that.

@Thaina
Copy link

Thaina commented Sep 5, 2018

This issue really takes long times

@Joe4evr
Copy link
Contributor

Joe4evr commented Oct 5, 2019

Namedropping CS0748 here because otherwise this issue is too hard to find.

@ViIvanov
Copy link
Contributor

Can I somehow help with moving this forward?

@CyrusNajmabadi
Copy link
Member

I'll champion htis. @vilvanov I could see us doing this. that said, having an high quality implementation provided by a community member would certainly make this cost less nad improve the chances of this happening.

@ViIvanov
Copy link
Contributor

Thank you for championing this.
I`m not sure my qualification is sufficient for implementing this, but I would start learning (I hope this does not prevent someone else from taking on the implementation at the best level).

@YairHalberstadt
Copy link
Contributor

@ViIvanov

If you need any help, feel free to ask on the Roslyn gitter. You can also make a draft PR early, and ask for guidance.

Feel free to tag me any time you like, as I've made a few contributions to Roslyn. @CyrusNajmabadi is always helpful and knows Roslyn inside out!

Good luck!

@333fred 333fred added this to the 10.0 candidate milestone Jul 13, 2020
@333fred 333fred modified the milestones: 10.0 candidate, Any Time Jul 13, 2020
@333fred 333fred added the Needs Implementation The specification for this issue has been approved, it needs an implementation label Oct 15, 2020
@kofifus
Copy link

kofifus commented Jan 7, 2021

Showing another use case - A simplified version of restricted state access with side effects (locking):

public delegate void ActionRef<T>(ref T r1);
public delegate RES FuncRef<T, RES>(ref T r1);

public class LockedState<T>  {
  T Value;
  readonly object theLock = new object();

  public void Ref(ActionRef<T> f) { lock(theLock) f(ref Value);  }
  public TRES Ref<TRES>(FuncRef<T, TRES> f) { lock(theLock) return f(ref Value); }
}

public static void Main() {
  LockedState<SomeComplexType<DateTime, OtherComplexType<List<int>>>> State = new();

  // this is what I have to do ATM:	
  State.Ref((ref SomeComplexType<DateTime, OtherComplexType<List<int>>> v) => {  v = v.next();  }); 

  // but I would like to do this:
  State.Ref((ref var v) => { v = v.next();  });

  // or even better this:
  State.Ref(ref var v => { v = v.next();  });

  // or even better this:
  State.Ref((ref v) => { v = v.next(); });

  // or best this:
  State.Ref(ref v => { v = v.next(); });
}

@Rekkonnect
Copy link
Contributor

@CyrusNajmabadi this one does not have a proposal spec yet, does the issue need the needs approved spec label first before moving onto the prototype implementation itself?

@CyrusNajmabadi
Copy link
Member

does the issue need the needs approved spec label first before moving onto the prototype implementation itself?

Yes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature Request Needs Implementation The specification for this issue has been approved, it needs an implementation Proposal champion
Projects
None yet
Development

No branches or pull requests