Support the same declaration syntax for ref as for out

Topics: C# Language Design
May 22, 2014 at 1:09 PM
Edited May 22, 2014 at 1:26 PM
A minor proposal.

Sometimes you have a ref argument which is not required to have a specific value (yet), and just passing a default(T) is sufficient, and given that we can't overload the function to have both ref and out variants, it would be nice to support the same new syntax as out var. Eg, given:
void FuncWithRef<T>(ref T value) where T : class { 
    if (value == null) {
        ...
    } else {
        ...
    }
}
Proposed calling syntax
FuncWithRef(ref T x);
Which would expand to
T x = default(T);
FuncWithRef(ref x);
I use it quite heavily for pattern-matching, where some fields have specific values and others are unspecified. Some patterns have many ref arguments, but the use case is quite ugly even with the newer declaration expressions.
MyPattern(ref (var a = default(T1)), ref b, ref (var c = default(T3)));
I would like to simplify to:
MyPattern(ref T1 a, ref b, ref T3 c)
May 22, 2014 at 7:03 PM
It may be worth noting that the CLR does not distinguish between ref and out parameters. If C# calls an implementation of IDictionary<TKey,TValue>.TryGeValue(TKey key, out TValue value), it will assome that the method has written value, but if the method was implemented in a language other than C# there will in fact be no guarantee that it will have done so. This allows for such interesting things as:
struct foo {
  public int Moe,Larry;
  foo(IDictionary<int, foo> theDict)
  {
     theDict.TryGetValue(0, out this);
  }
}
If the passed-in implementation of TryGetValue doesn't happen to write anything to its out parameter, then a statement like myFoo = new Foo(someDict); might leave the value of foo untouched.
May 23, 2014 at 5:55 AM
Edited May 23, 2014 at 5:57 AM
I vote against it - it leads potentail to bugs where you forgot to initialize variable and compiler would not catch it (like it does today):
int x; //forgot to set initial value
Foo(x); //error: use of unassigned local variable (good)
Foo(ref x); //no error (bad)
May 23, 2014 at 3:33 PM
ghordynski wrote:
it leads potentail to bugs where you forgot to initialize variable and compiler would not catch it (like it does today)
Foo(ref x); //no error (bad)
I understand it so that Foo(ref x) would still throw use of unassigned variable, it would be the Foo(ref int x) that wouldn't throw.
May 23, 2014 at 3:50 PM
JanKucera wrote:
ghordynski wrote:
it leads potentail to bugs where you forgot to initialize variable and compiler would not catch it (like it does today)
Foo(ref x); //no error (bad)
I understand it so that Foo(ref x) would still throw use of unassigned variable, it would be the Foo(ref int x) that wouldn't throw.
I could see maybe Foo(ref int x = 0) but without that initial value it still runs afoul of the premise of that compiler error. Of course that was specifically mentioned as an "ugly solution."

Given the use-case perhaps this conversation should resume under the thread for pattern matching where they are specifically trying to design syntax and operators for this purpose.
May 24, 2014 at 4:26 PM
ghordynski wrote:
I vote against it - it leads potentail to bugs where you forgot to initialize variable and compiler would not catch it (like it does today):
As noted, the fact that the only difference between out and ref parameters is an attribute whose recognition is not mandated by CLS means that methods written in other languages may not always write to out parameters. If a parameter whose purpose is return information to the caller was declared as ref in an early version of a class (especially one written in a language other than C# which does not recognize out parameters), it would be helpful to have an attribute with which it could be tagged that would allow it to be called using out syntax, but would not break existing callers that use ref. Using out to call such a method should probably the compiler to store default(T) before the call, to allow for the possibility that the called function would be likely not to write it. Such behavior would probably have been helpful with IDictionary.TryGetValue(), but imposing it now on such existing methods would be a breaking change (code might rely upon the fact that methods in written in VB can leave such parameters undisturbed).
May 26, 2014 at 6:47 PM
Edited May 26, 2014 at 6:47 PM
@supercat: Thanks. I didn't realize out worked this way, which is good enough for my needs, and the ref idea can probably be dropped. The only issue is calling existing code which uses ref in the simplified syntax, but I guess I can live with it, and use out from now on.