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

Proposal: ?= Assignment operator #12606

Closed
pfresnay opened this issue Jul 19, 2016 · 17 comments
Closed

Proposal: ?= Assignment operator #12606

pfresnay opened this issue Jul 19, 2016 · 17 comments

Comments

@pfresnay
Copy link

Summary
New ?= operator

Usage

Before

string name;
public string Name{
    get{
        return name = name?? $"{firstName} {lastName}";
    }
}

After

string name;
public string Name{
    get{
        return name ?=  $"{firstName} {lastName}";
    }
}
@pfresnay pfresnay changed the title ?= Assignment operator Proposal: ?= Assignment operator Jul 19, 2016
@svick
Copy link
Contributor

svick commented Jul 19, 2016

This is a duplicate of #10249, only with slightly different syntax (?= vs. ??=).

@HaloFour
Copy link

Also #205, #3366, #5163, #10556 and #11394. I'm not opposed to the concept, but it seems like such a small gain for a relatively infrequently scenario. Does it warrant a new compound assignment operator? I don't think so.

@pfresnay
Copy link
Author

I agree that it is a duplicate of #10249. But I think it is a common pattern for lazy initialization :

the "verbose" way :

string name;
public string Name{
    get{
        if(name == null)
            name = $"{firstName} {lastName}";
        return name;
    }
}

Lazy<> class & C# 6 syntax can't help when firstName & lastName are instance members :

public string Name{ get; } = $"{firstName} {lastName}";

compiler output : A field initializer cannot reference the non-static field, method, or property

@bbarry
Copy link

bbarry commented Jul 19, 2016

Unlike every other similar operator, the right hand side wouldn't get evaluated sometimes, which is pretty strange. Also both assignment (=) and the null coalescing operator ?? are handled directly by the compiler in all cases, unlike every other form of combination assignment operator (which become function call assignments if the operator is overloaded). For example you can modify how += works by overloading the + operator in a class. In order to make the new operator work like the rest of them, you would have to either:

  1. change the way ?? works to evaluate both sides and allow it to be overloaded (read: never going to happen because it is a breaking change)

  2. create some overload construct that doesn't evaluate the second term until evaluation is requested:

    public static Thingy operator ??(Thingy lhs, Lazy<Thingy> rhs) ...
    

Or decide overloading ?? isn't worth doing and deal with yet another special case in the language in the new ??= operator in documentation and testing.

?? already has some nontrivial code in the compiler dealing with nullable value types which would also have to be dealt with here. It will probably be further complicated whenever non-nullable reference types are decided to be done if that ever happens. Adding an additional dimension to it for assignment makes those other things more complicated.

https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_NullCoalescingOperator.cs

@HaloFour
Copy link

@pfresnay

I think it is a common pattern for lazy initialization

I don't disagree. But you're not required to use the "verbose" syntax from your example. You can do the following:

string name;
public string Name {
    get {
        return (name = name ?? $"{firstName} {lastName}");
    }
}

@pfresnay
Copy link
Author

@HaloFour yes I use your shortcut, but i'm not very happy with it.

@bbarry : ok, I understand. I'm going to search for another proposal to lazy load a backing field property :)

@HaloFour
Copy link

@pfresnay

I can imagine that with source generators that it would be pretty easy to construct a way to declare a "lazy" read-only property without requiring any modifications to the language. It'd also be more capable since it would be possible to cater the implementation based on metadata, such as setting a concurrency policy:

[LazyProperty(LazyThreadSafetyMode.ExecutionAndPublication)]
public string Name {
    get { return $"{firstName} {lastName}"; }
}

// generates something like

private readonly object Name_Lock = new object();
private string string Name_Result = null;
private volatile bool Name_Published = false;
public replace string Name {
    get {
        if (Name_Published) {
            return Name_Result;
        }
        lock (Name_Lock) {
            if (Name_Published) {
                return Name_Result;
            }
            Name_Result = original;
            Name_Published = true;
        }
    }
}

@DavidArno
Copy link

Surely the lazy initialisation method used in the OP:

public string Name{
    get{
        return name = name?? $"{firstName} {lastName}";
    }
}

causes name to be overridden with itself on every subsequent call after the first one? Putting the assignment after the ?? seems a more performant way of doing lazy initialisation:

public string Name => name ?? (name = $"{firstName} {lastName}");

To my mind at least, this actually seems clearer than using the proposed ?= operator.

@pfresnay
Copy link
Author

@DavidArno great proposal, I omited the C# 6 property-getter => :-)

@jjvanzon
Copy link

jjvanzon commented Sep 10, 2016

Strong proponent of ?=

Every time I want to resolve a null to a practical default I have to use elaborate notation. Introducing the ?= operator also prevents the coding error blah = blah2 ?? someDefault;. Doing a lot of coding and code reviewing, a syntax that prevents an error from being made is a welcome addition.

I like DavidArno's suggestion for a better readable and better performing version without any language changes: instead of:

x = x ?? y;

Use:

x ?? (x = y);

I did not even think of that syntax. Yet, my mind still craves a ?= operator. Realizing that other people would also not think of DavidArno's syntax, introducing the ?= operator could under the hood execute the (possibly) more performant version of DavidArno, upping the performance of everybody's null-resolutions.

I get the arguments of bbarry regarding the compiler implementation and consistency with behavior of other operators. Perhaps one could make it easier on him or herself and ignore the issues. No operator overloading, no making a problem out of the right hand side not always being executed, or differences with for instance the &=, & and && operators' behavior. Let the compiler translate x ?= y to x ?? (x = y), and leave the rest up to the same code that already handles all the intricate issues with the ?? operator, so all the issues with get solved for both syntaxes.

@jnm2
Copy link
Contributor

jnm2 commented Sep 22, 2016

I would thumbs-up except that ?= grates on my sense of symmetry. To get the assignment version of every other operator you append =, so the assignment version of ?? should be ??= for consistency.

@alrz
Copy link
Member

alrz commented Sep 22, 2016

@jnm2 Except for |= and &= which are an assignment operator for && and || for booleans.

@HaloFour
Copy link

@alrz

No, they're assignment operator versions of & and |. Neither short-circuits.

@jnm2
Copy link
Contributor

jnm2 commented Sep 23, 2016

Not only is @HaloFour correct, I have long wished for the non-existent assignment versions of && and ||.

@DavidArno
Copy link

@jnm2,

I have long wished for the non-existent assignment versions of && and ||.

One solution to this is to adopt the "treat variables as immutable" mentality, then the need for &&= and ||= just goes away... 😀

@jnm2
Copy link
Contributor

jnm2 commented Sep 23, 2016

@DavidArno unless that's just not what I want to do. I prefer mutability and x ?? (x = CalculateX()) over Lazy<> any day. C# is multi-paradigm and more to my point, missing &&= feels like a lack of completeness.

@gafter
Copy link
Member

gafter commented Mar 24, 2017

Moved to dotnet/csharplang#34

@gafter gafter closed this as completed Mar 24, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants