-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Proposal: out var declaration #6183
Comments
To clarify:
|
Do we really need this with struct tuples and nullables? Why not enhance the stdlib with |
What about using tuples to capture out parameters as in F#?
|
@gafter Do I understand the use case right:
makes |
@FunctionalFirst That would totally change method invocation semantics for any value. However, with #6067 you can deconstruct the |
@alrz Only for methods with out parameters, and it's a good change as it improves the locality of return values. You can also deconstruct the tuple right away with pattern matching. |
@bbarry Not quite. The variable will be scoped to the "then" clause only (not the "else" clause) because that's the way pattern variables work. But correct: "out variables" in a local variable initializer do not escape to the enclosing scope. If you want it in the enclosing scope you know where to declare it. @orthoxerox We do no intend to revisit every existing API that uses out parameters, change the recommendation for designing APIs, nor deprecate out parameters @vladd You did not declare |
@gafter Do the same scoping rules apply to conditional operations? Probably wouldn't matter much unless another out declaration was used in the false expression. |
@HaloFour If you declare a pattern variable or an out variable in the condition of a ternary expression, it will be in scope throughout the enclosing statement (unless it is part of the condition of an if statement, in which case it won't be available in the else clause). However, in the case of a pattern variable, it is likely not to be definitely assigned within the third clause of the ternary expression: string s = "123";
var result = int.TryParse(s, out var value)
? value // OK, in scope
: value; // OK, but according to the spec for TryParse, always zero here. compare this to object o = 123;
var result = o is int value
? value // OK, in scope
: value; // error: in scope, but not definitely assigned |
Aren't the "then" and the else two branches of the same if statement? So, this:
will not be the same as:
I'm not worried about all the refactorings out there because this will be a new case. But it doesn't make sense to me. |
@paulomorgado Yes and yes. The reason for this rule is that we expect people to often write code like if (o is FooNode node && node.Whatever) { ... code using node ... } else
if (Something && o is BarNode node) { ... code using node ... } else
if (o is BazNode node) { ... code using node ... } and we don't want to force them to have to think up new names for every Also we don't want different but overlapping scoping rules for pattern variables and these "out" variables. And anyway Swift does it this way (for pattern variables) so it must be right 😉 |
@gafter, isn't that use case covered by pattern matching constructs like |
@paulomorgado No, not always, including in this example where the second if condition starts with a boolean expression. |
Why not? Why implement better syntactic constructs and then keep using the old ones? |
@orthoxerox Because there is lots of code using the recommended style, and we'd prefer not to jerk our customers around from one fad to another. I imagine some API authors may want to provide alternatives, but I also expect that we may elect to do as F# does and treat out parameters as tuple results, in which case API authors won't have anything to change. |
@gafter Point about scope taken, but again, why the immutability? Which advantage does it bring? This and |
@vladd This would be consistent with all range variables in LINQ, and all pattern variables. Given the limited scope, there isn't a lot of value in being able to mutate them. This is also consistent with a theme in a bunch of proposed features to make it easier to do things that are safe, rather than making the unsafe things easier to do than the safe alternatives. |
Oops! Missed that when I wrote it. I think we have two distinct cases here. On one hand, we have a condition where a variable is definitely assigned after its evaluation:
And the other hand a condition where a variable is only definitely assign if the condition is true:
Besides the fact that this difference might be hard to specify and/or implement, wouldn't you agree with me that the variable is expected to be in scope of the else in the first case but not in the second case? You might even argue that this:
although having an implementation similar to the first case, falls semantically on the second case. The bottom like is that I feel the, in the first case, the scope of On the other hand, I think that any variable declared on the scope of a pattern matching construct (switch or match) should be scoped to that branch. |
@paulomorgado We prefer not to have separate scoping rules for the two cases. If it comes to that we'd probably prefer to drop this feature. |
Something seems missing here with regards to scoping. Also the continuous mention of if/then/else is just confusing as every example assumes some a boolean return value. What about all the other cases? Examples:
I can't see how any of this would work if variable is not scoped to the enclosing scope. |
If you want the variable to be in scope in the enclosing block, then you declare it in the enclosing block. |
So in the first statement both
And in the 2nd statement, it would be a compiler error? |
You cannot use the same name ( |
#11566
The out variable can not be referred to, within the other parameters of the method.
Is the proposal the equivalent to the following?
If so, then to referred to restriction, isn't a restriction. Is the **our variable If M(out x, "FOO" ) Then
' ...
Else If m = 0 Then
' ...
Else
Console.Write(x)
End If |
Yes, the proposal would make this
or this
an error. The tentative plan is to use the same scoping rules as for pattern variables, which could be found here https://github.com/dotnet/roslyn/blob/features/outvar/docs/features/patterns.md |
If the out variable is gets defined into a scope local to the statement / expression. |
Is it possible to use only Because if we just use so int.TryParse("10",var i); Also I would like to have void RefParse(string number,ref int i)
{
int n;
if(int.TryParse(number,out n)) // i would be default value if string cannot be parsed
i = n;
}
// `ref var` difference from `out var` by = symbol
RefParse("10",var i = 0); |
But |
@phrohdoh That's what I said |
@gafter, a little off-topic set of questions about the syntax you wrote above:
is this a valid C# syntax? since which version? Is it equivalent to: BazNode node = null;
if(o is BasNode) node = new BazNode() ? What if when |
@jasonwilliams200OK No and No. It is not valid yet. They are some people here proposed to let it valid in next version (but I am on against faction) And what it is doing is more like this if(o is BasNode)
{
BazNode node = o as BasNode;
// use node here
} The purpose is to use object in |
@jasonwilliams200OK Think of if as introducing the variable into scope, with value as if it was of that type. |
As upcoming feature "pattern matching" is allowing Example: Before: int a;
int b; // variable b isn't used but declared only to store out-result
SomeMethod(out a, out b);
return a; After: int a;
SomeMethod(out a, out *); // "out *" is indicating that second out parameter isn't used.
// another example - we are simply testing variable "s" can be parsed to integer, out parameter value
// is ignored
var s = "12";
if (int.TryParse(s, out *)
{
Console.WriteLine($"{s} is integer");
} |
That's planned, although it may not make C# 7.0. Could be in a minor release, though: https://blogs.msdn.microsoft.com/dotnet/2016/08/24/whats-new-in-csharp-7-0/ |
I just saw this feature mentioned on an MSDN blog, and my first thought when I saw this: |
The syntax Also, the syntax is really void Foo(out int x) { ... }
void Foo(out string x) { ... }
Foo(out x); // is x an int or a string? |
This feature would allow a variable be declared at the same time that is it passed as an out parameter:
A variable declared this way is read-only and scoped to the enclosing statement. More specifically, the scope will be the same as for a pattern-variable introduced via pattern-matching.
You should be able to use the contextual keyword
var
for the variable's type, in which case the specification for overload resolution would have to be modified to account for that possibility.The text was updated successfully, but these errors were encountered: