This feature would allow a variable be declared at the same time that is it passed as an out parameter:
_argument-value_:
out_type_ _identifier_
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.
To clarify:
A variable declared this way is read-only and scoped to the enclosing statement.
if (int.TryParse(input, out var value))
{
// value here - readonly
}
else
{
// value not in scope
}
// value not in scope
...
bool test = int.TryParse(input, out var value);
// value not in scope
Do we really need this with struct tuples and nullables? Why not enhance the stdlib with int? Int.TryParse(string input) and (int quotient, int remainder) Math.DivRem(int dividend, int divisor) instead?
What about using tuples to capture out parameters as in F#?
let (b, dt) = System.DateTime.TryParse("12-20-04 12:21:00")
@gafter Do I understand the use case right:
var r = int.tryParse(str, out var n);
makes r non-readonly and n readonly? If so, both of them are "just function output arguments", why the difference?
@FunctionalFirst That would totally change method invocation semantics for any value. However, with #6067 you can deconstruct the out variable right away.
@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 r as having any special relation to a method invocation. It is an ordinary local variable placed into the enclosing scope. n, on the other hand, is defined by reference to the method invocation (the grammar ties it to _argument-value_) and is _not_ placed into the enclosing scope. If you want it mutable or in the enclosing scope, you know where to declare it.
@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:
var result = ThisOrThat("123", out var value)
? value
: value;
will not be the same as:
int result;
if (ThisOrThat("123", out var value))
{
result = value;
}
else
{
result = value;
}
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 node. This trick doesn't work in the ternary expression because the scope is expanded to the enclosing statement.
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 :wink:
@gafter, isn't that use case covered by pattern matching constructs like match and switch?
@paulomorgado No, not always, including in this example where the second if condition starts with a boolean expression.
@gafter: We do no intend to revisit every existing API that uses out parameters, change the recommendation for designing APIs, nor deprecate out parameters
Why not? Why implement better syntactic constructs and then keep using the old ones? out parameters are worse than proper return values from all standpoints. I'm not talking about removing the old APIs or even deprecating them in the next version, but about providing a more fluent alternative.
@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 let-expressions in LINQ seem to be the only places where readonlyness is implicit.
@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.
@gafter,
@paulomorgado No, not always, including in this example where the second if condition starts with a boolean expression.
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:
(ThisOrThat("123", out var value))
And the other hand a condition where a variable is only definitely assign if the condition is true:
(o is BazNode node)
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:
(o is Something(out a, out b))
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 value should be the if statement. And I don't see any restrictions or caveats of being that way that are not present today.
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:
DivMod(num, den, out var div, out var mod);
Foo(Bar(out var baz), baz);
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.
@gafter:
So in the first statement both div and mod will not be accessible, but still compile? If so, would the following (non-sensical statement) be allowed?
DivMod(num, den, out var _, out var _);
And in the 2nd statement, it would be a compiler error?
You cannot use the same name (_) to declare two different variables in the same scope. But there is no problem with your example Foo(Bar(out var baz), baz); as the scope of baz is the enclosing statement.
What about ref variable initialized with default value?
try { Monitor.Enter(lockObject, ref var acquiredLock); }
finally { if (acquiredLock) Monitor.Exit(lockObject); }
I think that would make sense to relax variable scope in try and if to support similar scenarios.
@alrz
What about ref variable initialized with default value?
I'd prefer not to. Even in your example, a variable defined in a try block isn't in scope in a finally block.
I'd prefer not to.
why not?
a variable defined in a try block isn't in scope in a finally block.
I know, I'm suggesting that it should be.
And I've no idea why Monitor.Enter is using ref.
I'd prefer not to because it is too magical giving local variables initial values out of thin air. I also think your proposed scoping is contrary to the language's design principles. Also I would prefer that variables introduced in expressions be readonly for safety and simplicity. If your intent is to share mutable state across statements, the declaration should be explicit.
@alrz
And I've no idea why
Monitor.Enteris usingref.
It's meant to be used like this (quoting example from the doc on MSDN):
``` c#
bool acquiredLock = false;
try
{
Monitor.Enter(lockObject, ref acquiredLock);
// Code that accesses resources that are protected by the lock.
}
finally
{
if (acquiredLock)
{
Monitor.Exit(lockObject);
}
}
```
If the method used out instead of ref, this would still work. But it would also mean you wouldn't have to initialize acquiredLock, but then you wouldn't be able to access it in the finally (it wouldn't be definitely assigned).
This means that changing from ref to out would only allow coding patterns that don't make sense in the context, so I think ref makes sense.
From Eric's blog:
var seq = new List<string> { "1", "blah", "3" }; int tmp; var nums = from item in seq let success = int.TryParse(item, out tmp) select success ? tmp : 0;
The question is, would this possible with out var?
var nums =
from item in seq
let success = int.TryParse(item, out var tmp)
select success ? tmp : 0;
or the issue remains?
Maybe
var numc =
from item in seq
select int.TryParse(item, out var tmp) ? tmp : 0;
No, it would not be allowed. A pattern variable introduced in an expression lambda has the scope of the expression lambda's body. And queries are defined, semantically, so that these expressions are interpreted as expression lambda bodies.
In any case it would not do what you want, as a query may be and usually is evaluated lazily or concurrently. Even if we allowed it, your tmp variable would not be definitely assigned where you use it last (since the compiler cannot guarantee that int.TryParse is ever called.
Yes, that would work nicely. Or just
var nums =
from item in seq select int.TryParse(item, out var tmp) ? tmp : 0;
or even
var nums =
seq.Select(item => int.TryParse(item, out var tmp) ? tmp : 0);
Did you know that int.TryParse is specified to produce zero in its out parameter when it returns false?
@gafter Then
seq.Select(item => (int.TryParse(item, out var tmp) ; tmp) );
It's almost 2016 :smile:
@gafter
No, not always, including in this example where the second if condition starts with a boolean expression.
That's where case guards are useful in both switch statement and match expressions.
switch(o) {
case FooNode node when node.Whatever: ... break;
case BarNode node when Something: ... break;
case BazNode node: ... break;
}
While this scoping does make sense for patterns and is (because it returns a bool indicating that variables are definitely assigned) but this is not the case with out variables. And I think it would be really confusing for out var variables to be not in the scope in the consequent statements.
F(out var foo, out var bar);
// foo and bar are not in scope? no way!
The thing is, that out variables are not restricted to TrySomething methods, I can think of many Win32 API or any other native or managed functions that would use out parameters to return multiple results, right? This scoping makes them less usable IMO.
How is this to be written for custom types that have TryParse* methods?
out var doesn't at all indicate a type.
@Phrohdoh It is statically inferred, but according to the spec you still can explicitly specify the type e.g. int.TryParse(str, out int value);.
This proposal is a bad one and hopefully will not make it into C# 7. It will make for some seriously ugly pattern matches. With an Option<T> version of TryParse, I can pattern match on it in a meaningful way:
switch (int.TryParse(someString))
{
case Some(var x): DoSomethingWithX(x);
default: Console.WriteLIne($"{someString} isn't an int.");
}
Whereas, if you try that with out parameters:
switch (int.TryParseInt(someString, out var x))
{
case true: DoSomethingWithX(x); // case true? WT...!
default: Console.WriteLIne($"{someString} isn't an int.");
}
I accept that out parameters cannot be deprecated, but we absolutely should not be encouraging their use by adding syntactic sugar around their use in C# 7. This is a huge backward step in improving the language.
@DavidArno
Shoe-horning switch into the conversation is the problem with your code samples.
if (int.tryParse(someString, out var x)) {
DoSomethingWithX(x);
}
else {
Console.WriteLine($"{someString} isn't an int.");
}
@DavidArno
With an Option
version of TryParse
There ain't no such thing as Option<T> version of TryParse and nor Option<T> does exist. But we do have Try* methods which are an accepted convention through the whole BCL. So this proposal would make them easier to work with.
@alrz,
That was my point. Assuming C# 7 introduces discriminated unions/ADTs, then just like with F#, there becomes a significant case for baking Option<T> in as it will be a commonly used type. We can't get rid of the Try* methods from the BCL as that would break legacy code. That is no excuse to make them easier to work with though. In my opinion at least, this is a syntactic "painting lipstick on a pig" feature and thus, as I said, a bad idea.
Rereading this proposal I've realized it is the equivalent of Rust's Some(m) => m assignment for the enclosing statement, please correct me if I am wrong about that.
In Rust the type is inferred from the match target (obviously only applicable when discussing pattern matching), which is nice there but in C# it is better to be explicit (even if that means explicitly asking the compiler to imply) which allows casting.
This would allow us to stop creating trash variables for each TryParse and would also close the scope for the output variable quicker, both of which I see as a gain.
Can you clarify this?
- There is a proposal pending LDM decision: An out variable may not be referenced before the close parenthesis of the invocation in which it is defined:
The out variable can not be referred to, within the other parameters of the method.
M(out x, x = 3, 0); // error
Is the proposal the equivalent to the following?
M( out x, x = 3)
// Equivalent to
int x;
M( out x , x = 3 )
If so, then to referred to restriction, isn't a restriction.
Is the **our variableaccessible with then other branches of theif`?
vb.net
If M(out x, "FOO" ) Then
' ...
Else If m = 0 Then
' ...
Else
Console.Write(x)
End If
Yes, the proposal would make this
M(out int x, x = 3, 0); // error
or this
M1(out int x, M2(M3(out x), x))
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.
Does that mean it overloads one that is already defined?
Or would that also be an error?
Is it possible to use only var at out var
Because if we just use var at function parameter it should be obvious that it is used for out so we could drop out and just use var instead of full out var
so
``` C#
int.TryParse("10",var i);
Also I would like to have `ref var` like this
``` C#
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 RefParse doesn't have an out parameter so using var there doesn't make sense even with your example. Right?
@Phrohdoh That's what I said // 'ref var' difference from 'out var' by = symbol so both use the same var but of ref you need to declare value while out is not
@gafter, a little off-topic set of questions about the syntax you wrote above:
o is BazNode node
is this a valid C# syntax? since which version? Is it equivalent to:
c#
BazNode node = null;
if(o is BasNode) node = new BazNode()
?
What if when BazNode has no ctor accepting zero params, would that magical syntax fail?
@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
C#
if(o is BasNode)
{
BazNode node = o as BasNode;
// use node here
}
The purpose is to use object in if scope as short as possible
@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 * pattern (match anything), it would be applicable to use same literal for out parameter that isn't interesting and should be ignored.
Example:
Before:
``` C#
int a;
int b; // variable b isn't used but declared only to store out-result
SomeMethod(out a, out b);
return a;
After:
``` C#
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");
}
@Opiumtm
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:
p.GetCoordinates(out var x, out var y);
Was - why type the 'var'? Would there be some additional ambiguity or something if we just accepted:
p.GetCoordinates(out x, out y);
To mean the same thing?
@BruceWilliams
The syntax out <identifier> is already legal syntax. If the compiler were changed so that it would create a new variable if one wasn't already in scope that could lead to issues where a typo would result in accidentally create a new out declaration variable than populate the existing variable.
Also, the syntax is really out <type> <identifier>. var may be used in place of the type where there is no ambiguity, but since methods can overload on out parameters if the type was implied it would never be possible to resolve that ambiguity:
void Foo(out int x) { ... }
void Foo(out string x) { ... }
Foo(out x); // is x an int or a string?
Most helpful comment
What about using tuples to capture out parameters as in F#?