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: Add support for ternary ref expression #16473
Comments
I like it. I wonder whether the |
@gafter the foo(ref <ternary here>) // I am passing by reference
foo( ternary here ) // I am passing by value If we infer Also there could be cases where ref ternary could be used in ambiguous ref/val contexts - argument of a dynamic/overloaded call, assignments to ref variable (potential future feature). I think requiring outer ref, when used in ref context, is more consistent with the rest of the language. |
It looks like a ref to a ref, You take a ref to a variable not expression, I think void M(ref int i) {
ref int a = ref i;
M(ref a);
} You might argue that this is because it's more readable. But C# already chose |
@alrz - I am not sure I completely understand your comment. C# uses Example: class Program
{
static void Main()
{
int i = 42;
var o = new Derived();
// pass by value
o.Test(i);
// pass by ref
o.Test(ref i);
dynamic d = new Derived();
// pass by value
d.Test(i);
// pass by ref
d.Test(ref i);
}
class Base
{
public void Test(ref int x)
{
System.Console.WriteLine("ref");
}
}
class Derived: Base
{
public void Test(int x)
{
System.Console.WriteLine("val");
}
}
} |
Ok, how is this ambigious? int i = 42;
var o = new Derived();
// pass by value
o.Test(i);
// pass by ref
ref int r = ref i;
o.Test(r);
dynamic d = new Derived();
// pass by value
d.Test(i);
// pass by ref
ref int r = ref i;
d.Test(r); I understand C# uses ref when an operand is passed by reference, that was when we didn't have ref locals and it just imitated the syntax from C++ but with a f(&i); // take and pass address Now that we have ref locals, we could follow the same model, int* r = &i; // take address
f(r); // pass address |
That's a pointer though. C# |
When you declare int z = 0;
ref int r = ref z; and you call a method public void M(ref int i); we require you write M(ref r); so that we can distinguish it from a call to the method public void M(int i); which you would write M(r); For language uniformity, we also require int ref r = ref z; This enables us to distinguish assigning through the reference r = 3; from (a possible future feature) re-assigning the reference itself ref r = ref q; |
So I think "alias" is more appropriate for that. I've got confused with a reference not being a reference. |
IDE does not have cost-concerns here. This seems nice and a totally natural continuation of what we've done with 'ref' so far. |
Proposal for the "conditional ref expression" feature. moved from dotnet/roslyn#16473
Proposal for the "conditional ref expression" feature. moved from dotnet/roslyn#16473
I'll go ahead and close this issue, since this was done in C# 7.2. |
Was it? Isn't this code supposed to work then? (it doesn't) bool b = false;
int x = 1;
int y = 2;
ref int z = b ? ref x : ref y; Or maybe I misunderstood what this issue is about... |
You are missing a ref int z = ref b ? ref x : ref y; |
Ah, thanks @benaadams. That seems a bit redundant, but in a way, it makes sense... |
Doesn't seem to work with a switch expression, though... int i = 1;
int x = 123;
int y = 456;
int z = 789;
ref int r = ref i switch
{
1 => ref x,
2 => ref y,
3 => ref z
_ => throw new Exception("oops");
}; is this supported? |
No, there is no |
Is there already a feature request for that? I was just thinking about using the new switch expression to replace a clunky nested ref ternary expression like that. |
The pattern of binding a ref variable to one or another expression conditionally is not currently expressible in C#.
The typical workaround is to introduce a method like:
Note that this is not an exact replacement of a ternary since all arguments must be evaluated at the call site.
The following will not work as expected:
The proposed syntax would look like:
The above attempt with "Choice" can be correctly written using ref ternary as:
The difference from Choice is that consequence and alternative expressions are accessed in a truly conditional manner, so we do not see a crash if
arr == null
The ternary ref is just a ternary where both alternative and consequence are refs. It will naturally require that consequence/alternative operands are LValues.
It will also require that consequence and alternative have types that are identity convertible to each other.
The type of the expression will be computed similarly to the one for the regular ternary. I.E. in a case if consequence and alternative have identity convertible, but different types, the existing type-merging rules will apply.
Safe-to-return will be assumed conservatively from the conditional operands. If either is unsafe to return the whole thing is unsafe to return.
Ref ternary is an LValue and as such it can be passed/assigned/returned by reference;
Being an LValue, it can also be assigned to.
Ref ternary can be used in a regular (not ref) context as well. Although it would not be common since you could as well just use a regular ternary.
=======================
Implementation notes:
The complexity of the implementation would seem to be the size of a moderate-to-large bug fix. - I.E not very expensive.
I do not think we need any changes to the syntax or parsing.
There is no effect on metadata or interop. The feature is completely expression based.
No effect on debugging/PDB either
The text was updated successfully, but these errors were encountered: