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: Semi-auto-properties with setters [topic has evolved] #8364

Closed
lachbaer opened this issue Feb 3, 2016 · 62 comments
Closed

Proposal: Semi-auto-properties with setters [topic has evolved] #8364

lachbaer opened this issue Feb 3, 2016 · 62 comments

Comments

@lachbaer
Copy link
Contributor

lachbaer commented Feb 3, 2016

Status 2016-03-02

This topic has evolve/changed from providing a name for the backing field to extending the auto properties of C# with setters, thus providing semi-auto-properties.

The initial proposal was droped on that way.


Original topic start

According to an Expression Body Definition of a property with the lambda operator it would be consistent to allow the => operator also in the get section of a property

        private string _customHelloMessage = null;

        public string HelloMessage
        {
            get => _customHelloMessage ?? "Hello World";
            set
            {
                _customHelloMessage = value;
            }
        }

This would be consistent as the lambda operator introduces a return expression.

Having this said, it would also be nice to shorten the set section of the property. Here I have some ideas.

For the following samples I assume that there is a field private string _customHelloMessage = null;

First we could also use the => operator, but I don't think that is a good idea as set is not a return expression. Just for an optical impression:

        public string HelloMessage
        {
            get => _customHelloMessage ?? "Hello World";
            set => _customHelloMessage = value;
        }

Looks better, but the only thing that makes a real differnce is the letter s or r in get and set. Also, as said, it is not consistent to the current use of =>.

The next idea is to extent the contextual keyword value to stand with set:

        public string HelloMessage
        {
            get => _customHelloMessage ?? "Hello World";
            set value _customHelloMessage;
        }

It looks a bit odd, but should be intuitive.

This idea could be extended for the case that you want to specify the backup field by yourself:

        public string HelloMessage
        {
            get value _customHelloMessage;
            private set value _customHelloMessage;
        } = "Hello";

Also note the implicit initialization, that sould be further possible for auto-implemented properties;

The above sample could be abbrevated even more with the value keyword declaring the backup field within the property definition:

        public string HelloMessage
        {
            value _customHelloMessage;
            get; 
            set;
        } = "Hello";

what we could even more shorten with

        public string HelloMessage { value _customHelloMessage; } = "Hello";

In case there should be modifiers, get and set must be explicitly stated.

        public string HelloMessage { value _customHelloMessage; get; private set; } = "Hello";

The get and set methods can be overwritten, but at least one must be 'default' (get; or set;) to make sense for the value being there, if it's there.

A backup field must never be implied automatically by the compiler, meaning the _customHelloMessage needs an explicit declaration. Otherwise it will be error-prone.

I hope this is a worthy idea.

Regards,
Ike

@dsaf
Copy link

dsaf commented Feb 3, 2016

#7881 #850

@lachbaer
Copy link
Contributor Author

lachbaer commented Feb 3, 2016

I like my suggestion more ;-) No, really! :) Think, that it's a neat re-use of the value keyword.

Also I like the idea of Property-scoped fields #850 ! But it stands in no contrast to this issue.

@gwenzek
Copy link

gwenzek commented Feb 3, 2016

public string HelloMessage { value _customHelloMessage; } = "Hello";

Really neat !

@lachbaer
Copy link
Contributor Author

lachbaer commented Feb 3, 2016

Addition:

...
value _backingField;
...

should only be present 1 time, either after set or get or standalone. Specifying it twice (like in a sample above) is either redundant or again error-prone:

public string HelloMessage
        {
            get value _backing_ONE;
            private set value _backing_TWO;  // WTF, why 'two'?
        }

There might be cases where you want this intentionally, but I recommend using the good old get {...} set {...} for that scenario.

Having value either after get or set or standalone supports reading code in case you have something like this:

public string HelloMessage
        {
            get value _backingField;
            set {
                if (value == null) throw new ArgumentNullException();
                set value; // tells compiler to write to assign to declared _backingField
            }
        }

Please notice the set value; expression. Just in case you rewrite your code it ensures that the property value is always written to the correct, specified backing field. This is in direct accordance with the set value; outside the code block but inside the property.

@HaloFour
Copy link

HaloFour commented Feb 3, 2016

The lambda (=>) operator is already valid in void methods so I don't see why that would be an issue:

private int x;
public void set_X(int value) => this.x = value;

The value keyword already refers to the proposed value in the setter accessor method. Having it do double duty as the backing field and the proposed value would be very confusing. set value in particular, as what are you setting, the backing field or the proposed value, the latter of which is referenced by the keyword value.

I really don't understand the point of specifying the backing fields at the scope of the accessors. get value _backingField doesn't come close to resembling a declaration and doesn't describe anything about the field. Scope-wise it doesn't make sense for something declared in the getter accessor to be accessible in the setter accessor, and it makes even less sense for the setter accessor to declare yet another backing field.

This proposal just seems like a syntactic jumble. I certainly prefer the syntax proposed in #850, at least where fields can be declared within the scope of the property.

@lachbaer
Copy link
Contributor Author

lachbaer commented Feb 3, 2016

@HaloFour you probably got me wrong. get value _backingField is an abbrevation of value _backingField; get;.

The _backingField is not a declaration, but the name of the backing variable. This could be a normal instance variable or according to #850 a property-scoped field.

value variable_name; in the scope of the first property block gives the compiler order to use variable_name as the (self declared) backing field instead of generating an own. This seems very consistent to me as e.g. string myStringVariable; declares myStringVariable for holding a string and value backingVariable; declares backingVariable holding the property value.

For set value; within a code block I agree with you (partly ;-) after giving it some thoughts. :-)

Maybe a single set; would suffice to instruct the compiler to put value in the backing field?
See this:

  public string AnotherMessage {
    get; // implicitly created getter, we don't have access to the backing field!
         // unless of course no 'value ...' instruction is given before 'get;'
         // This short 'get;' allone tells the compiler to create an auto-property.
    set { 
        TestForNullValue(); // e.g.
        set; // put the value in the backing field, though we don't know it.
    } 
  } = "Hello again";

value is a contextual keyword, valid only within the set accessor of a property declaration. So imho there is nothing against making set the same!

I also agree using the => lambda operator for set operations (#7881) , though primarily intended for returning values, syntactically it is already correct for void delegates().

So, after all for me it get's down to

   public string HelloMessage { value _customHelloMessage; get; private set; } = "Hello";
   // or having 'get' and 'set' implicity implemented when ommited:
   public string HelloMessage { value _customHelloMessage; } = "Hello";

   public string AnotherMessage { get; set { doSomething(); set; } }

dropping off all of this get value variable; set value; thingy.

Should I open a new issue for this revised idea?

@lachbaer lachbaer changed the title Further consistent shortening for getter and setter Expression syntax for properties with 'value' keyword Feb 3, 2016
@HaloFour
Copy link

HaloFour commented Feb 3, 2016

I think it's fine for a proposal to evolve in place.

In effect it is a declaration if the result is an instance field. But I don't see a real use for having it if you don't intend to use it directly as an identifier.

I'm personally not a big fan of new syntax that straddles normal properties and auto-properties. I think it confuses the reason as to why you'd use one or the other. But let's break this down into the use cases. Namely, it's about being able to add custom validation or logic to the accessor, most likely the setter accessor. Currently that requires going from:

public string Foo { get; set; }

to

private string foo;
public string Foo {
    get { return foo; }
    set {
        if (value == null) throw new ArgumentNullException("value");
        foo = value;
    }
}

That is big jump in verbosity and it is understandable to want to find some kind of short-hand. Perhaps something like the following:

public string Foo {
    get;
    set {
        if (value == null) throw new ArgumentNullException("value");
        set;
    }
}

Although does that honestly buy you much? You eliminate the getter boilerplate, the instance field declaration and the manual assignment, but the property is still almost as long. And is it really clear what the property is going to do? I'm not completely sold, but I can see value in going down that path.

As for syntax for declaring the backing field, I don't think that has any value unless you actually use that field directly. I don't see proposals like #850 as being about verbosity but rather about visibility; trying to prevent accidental direct assignment of the backing field. I wouldn't expect a field declared within the body of a property to be accessible anywhere else in that class. So the two examples where you define the backing field and never refer to it directly in the accessors has no value to me.

@leppie
Copy link
Contributor

leppie commented Feb 3, 2016

How about just:

string bar; // a field
public string Foo => bar, x => bar = x;

Or more extreme:

string bar; // a field
public string Foo => bar, bar = value; // as if value => bar = value

@HaloFour
Copy link

HaloFour commented Feb 3, 2016

Extreme is right. Definitely on the wrong side of the succinct/terse spectrum in my opinion. Do we really need a syntax shorter than auto-properties? What is bar supposed to mean?

@leppie
Copy link
Contributor

leppie commented Feb 3, 2016

@HaloFour: the backing field (in that example)

@HaloFour
Copy link

HaloFour commented Feb 3, 2016

Declared elsewhere or entirely implicit? If the latter what is its visibility? Type?

@leppie
Copy link
Contributor

leppie commented Feb 3, 2016

@HaloFour backing field was a bit wrong terminology :D I meant just any field (but you one you use to back the property with)

Updated example

@HaloFour
Copy link

HaloFour commented Feb 3, 2016

@leppie I think that the term is appropriate, it just didn't clear up the confusion I had as to where it was declared. 😄

I think that it's a pretty far departure to property syntax today. I think that I'd prefer something more akin to:

private string bar;
public string Foo { get => bar; set => bar = value; }

Which is effectively just normal properties combined with expression members.

@lachbaer
Copy link
Contributor Author

lachbaer commented Feb 3, 2016

In your last sample you wrote 3 times 'bar'. This is slightly shorter, especially if you want many properties with own backing variables:

private string bar1;
private string bar2;
private string bar3;
//...
private string bar19;
private string bar20;

public string Foo1 { value bar1; }
public string Foo2 { value bar2; }
public string Foo3 { value bar3; }
// ...
public string Foo19 { value bar3; /* uses field bar3 for some reason */ }
public string Foo20 { value bar20; }

One could also use value in the getter

private int _counter;
public int Counter {
    value _counter;
    get {
        value = CalculateSomething(value);
        return value;
    }
    private set;
}

When you change your mind and you want to switch from _counter to base.counter then guess where and what you only have to change.

I think it even gets more intersting when we reference other properties instead of fields with the value keyword.

public string UnfancyProp { get; set; }
public string FancyProp {
    value UnfancyProp;
    get;
    set => "Fance me up " + value;

Writing this brings me back to the lambda operator => for the set part:
It should be used for a return value, i.e. the value that is writte to the backing field:

public string AutoProp {  // "standard, old fashioned" auto-property
   get;
   set => "Hello " + value;
}

If one doesn't want to set a value to the backing or wants to do some other important stuff, she should use the common set { ... }.

@HaloFour
Copy link

HaloFour commented Feb 3, 2016

@lachbaer

Why would you ever want to use that syntax? It makes no sense to declare a backing field and a property that does nothing more than wraps that backing field. We already have auto-properties.

Using value to alias fields/properties/whatever doesn't sound like a good idea. You save a few keystrokes but now you have to look in multiple places to find out what that property is supposed to be doing and the semantics could change depending on what was being aliased, e.g. passing value to a function as a ref argument takes on a very different meaning if value is a field vs. a property. I'd rather type the extra keystrokes and declare my intent more apparently.

I don't see the use cases here.

@lachbaer
Copy link
Contributor Author

lachbaer commented Feb 3, 2016

You're giving me headaches :-D ;-)
What I want to achieve is to specify the backing field of (semi-)-auto-properties. By now you cannot secify an auto-property with even some lightweight code, as there is definitely use for it! Let's get rid of the 'value-definition' for the moment.

public string Foo {
    get;
    set => { if (value==null) throw new ArgumentNullException(); return value; }
}

here we have an auto property where we are not really interested in the backing field, just in the argument checking. Without auto-property it is slightly more cumbersome to write.
But where should set put the returned value? get; gives the hint of an semiauto-property.
But what if this hint (could be set; also) is not given?

Let's try another syntax, still c-sharpy, because the property kind of 'inherits' the backing field:

private string _backingField;
public string Foo {
    get => "Foo is " + value ; // oops, no more hints for an auto-prop!!!
    set => { if (value==null) throw new ArgumentNullException(); return value; }
} : _backingField = "Initial string";

Now it is obvious what value refers to, without the value keyword before the get. ;-)
@HaloFour look at this code snippet.

private T CheckNull<T> (T myvalue) {
    if (myvalue==null) throw new ArgumentNullException();
    return myvalue;
}
/* complete auto-property, just with a bit of setter logic */
public string FirstName{ get; set => CheckNull<string>(value) } = "";

/* here no auto-property can be created, because getter and setter are defined,
   therefore we reference the backing field */
private string _lastName ;
public string LastName{
    get {
        Console.WriteLine("Another get on family " + value);
        return value;
    }
    set => CheckNull<string>(value);
} : _lastName = "";
public string FullName { get => FirstName + " " + _lastName ; }

And if this is too much code for a sample, here's an auto-property with explicit backing field:

private int _bar;
public int Foo { get; set; } : _bar;
public var Foo2 { get; set; } : _bar; // type inference, looks ugly...

@HaloFour
Copy link

HaloFour commented Feb 3, 2016

@lachbaer

I get what you want. What I don't understand is why. If you're going to the trouble of defining a backing field then you have no reason to use auto-implemented properties. Trying to hammer them together makes little sense and solves no problems.

As for trying to add logic into auto-implemented properties, I must note that the charter for auto-implemented properties is to provide a concise syntax when no additional logic is required in the property accessors. I'm largely of the opinion that trying to add logic back into auto-implemented properties doesn't make a lot of sense particularly since it's quite easy to define a full-fledged property.

That said I think that there may be some compelling cases to trying to squeak validation into an auto-implemented property setter accessor but only if the syntax is concise enough and clear enough and I haven't seen anything that I think fits the bill.

@lachbaer
Copy link
Contributor Author

lachbaer commented Feb 3, 2016

A little summary by now:

  • expression syntax for get and set must always return a value
  • the return value for get is the readout of the property (obviously)
  • the return value for set is the value stored in the backing field of the property (so set; is a shortcut for set => value;)
  • if either set; or get; is stated, an auto-property is created
  • if both set and get have a body, then no auto-property is created.
/* this is still an auto-property */
public int Foo { get; set => (value<0) : 0 : value; } 
  • the value keyword can be used in a get code block (or expression) to access the current value of the property backing field
  • in a get body value must not be an L-value, so the backing field is writable only through the setter
  • in a set body the current value can only be read through the getter

For self-defined properties (not auto), if you want to use the expression syntax on set or want to use the value keyword in a get section, then you must specify the name of the backing field after the property's closing brace with a :.

private int _foo;
public int Foo { get => value; set => (value<0) ? 0 : value; } : _foo;

Assigning a backing field manually to an auto-property could be done, but actually makes no sense. But maybe it might be easier to implement it in the complier that to 'explement' it ;-)

protected int _foo;
public int Foo { get; protected set; } : _foo;

@lachbaer
Copy link
Contributor Author

lachbaer commented Feb 3, 2016

@HaloFour
You ask, why I want to do that. Well, many things in C# are syntactic sugar, since the first versions.
Image following scenario:

private T CheckNull<T> (T myvalue) {
    if (myvalue==null) throw new ArgumentNullException();
    return myvalue;
}

public string MrMrs { get; set => CheckNull<string>(value) } = "";
public string FirstName{ get; set => CheckNull<string>(value) } = "";
public string MiddleName{ get; set => CheckNull<string>(value) } = "";
public string LastName { get; set => CheckNull<string>(value) } = "";
public string Address1{ get; set => CheckNull<string>(value) } = "";
public string Address2{ get; set => CheckNull<string>(value) } = "";
public string ZipCode{ get; set => CheckNull<string>(value) } = "";
public string City { get; set => CheckNull<string>(value) } = "";
public string Country { get; set => CheckNull<string>(value) } = "";
public int Age { get; set => (value<0) ? 0 : value; };

This was written really fast with copy-paste.

Specifying the backing field and using value instead in the code reduces typos and eases code refacturing.

@HaloFour
Copy link

HaloFour commented Feb 3, 2016

@lachbaer

Having the setter accessor expression require a returned value doesn't make a lot of sense given that setter accessors return void. I understand that you're trying to define the expression as the assignment to the backing field itself but given that the behavior is inherently different I think it would be quite confusing. Not to mention it makes it absolutely impossible to simply not assign the backing field via return. Assigning the backing field to its own value isn't the same thing and could cause unexpected thread-safety issues.

@lachbaer
Copy link
Contributor Author

lachbaer commented Feb 3, 2016

@HaloFour
I don't want to replace the old syntax where set has a { ... } block. Here no return value is necessary.
But why using an => expression instead of { ... } when they are identical in the outcome? That's why I thought it would be more consistent to use => with a return value. As with the new with on pattern matching.

@HaloFour
Copy link

HaloFour commented Feb 3, 2016

It's not consistent. To be consistent it should behave as a property setter is supposed to, including having a void return parameter. It should be consistent with using member expressions on any other void method. Making it an expression that returns a value makes it quite inconsistent.

@lachbaer
Copy link
Contributor Author

lachbaer commented Feb 3, 2016

Can you rewrite the above addess sample without set => ... for assinging the value by return? Maybe I oversee something?

@HaloFour
Copy link

HaloFour commented Feb 3, 2016

@lachbaer

Not with auto-implemented property syntax given that there is no way to directly access the backing field. You could potentially add a new contextual keyword to reference it but that doesn't seem terribly useful, not to mention confusing if that keyword is in use in any other way.

With normal properties, you could potentially do the following:

private void CheckNull<T>(T proposed, ref T field) {
    if (proposed == null) throw new ArgumentNullException();
    field = proposed;
}

private string name;
public string Name { get => name; set => CheckNull(value, ref name); }

That said, I figure that expression member syntax would apply more to the getter than the setter.

@lachbaer
Copy link
Contributor Author

lachbaer commented Feb 3, 2016

I still stick with the basic idea of my proposal. Auto-properties were introduced, because they were shortening code a lot. Expression body methods and properties were introduced with C# 6 to shorten this. Of course this could all be done with the old language features, but what's the idea behind evolving a language? There are already some easements, like Expression Body Definitions [(https://msdn.microsoft.com/en-us/library/x9fsa0sw.aspx#Anchor_0)]. We can go some steps further.

You might be right that declaring an external backing field and still using coding shortcuts is nothing that would be done often in real code, probably never seriously. So I drop this idea for the moment.

Let's focus on having more control over auto-props, thus reducing the need for an instanciated backing variable:

private string _customHelloMessage = null;
public string HelloMessage
{
    get { return _customHelloMessage ?? "Hello World"; }
    set { _customHelloMessage = value; }
}

This is already using new C# features, but nevertheless it is quite long for achieving a small thing. Actually I don't really need the _customHelloMessage field and it annoys me seeing it in my Intellisense list.

With my idea we could shorten this long code a lot!

public string HelloMessage
{
    get => value ?? "Hello World";
    set;
} = null;

Next I want to check for null in the setter

public string HelloMessage
{
    get;
    set => value ?? "Hello world";
} = null;  // initial value goes through setter for integrity!

I think we agree that set => only makes sense on auto-properties or when we declare a backing field (what we don't want to do any more outside of the property scope ;-).

Getting back to the idea of #850, my proposal for the use of the value keyword eliminates the need for a property-scoped variable as backing purpose.

Now there might be scenarios, where you still want some more control over your code, but you also want kind of an auto-prop. This might be the case if your code grows. Here I introduce the new contextual keyword let, that instructs the compiler to generate an (unnamed) backing field, like for auto-props.

public virtual int Foo
{
    let backingField = 0; // creates a backing field with the appropriate type, optionally initializes it.
    get;
    set => backingField + value;
} = 12; // initializes again, but using the setter this time

// one useless sample to make it clear ;)
public int Counter {
    let mycounter = 0;
    get => ++mycounter;
    set => 0;  // any set call resets the counter
}

let now creates a backing field that will not list anywhere in my code (again, see #850).

Derived classes don't have access to the property internal backing field of the base class. It would not be intuitive in the derived classes and mess things up.

Résumé:
This could make code significantly shorter for many all day routines as well improving stability, because there is no more need to declare an instance backing field that could be accessed from anywhere within the class or even worse the whole code (when accidentially being public).

Any occurence of get;, set;, set => or let ... indicates to the compiler to create an 'auto-property', that can be customized.

@HaloFour
Copy link

HaloFour commented Feb 3, 2016

Shorter code is not always better code. Clarity is significantly more important. Code will be read (by humans) significantly more often than it will be written, so the language should always optimize for that case.

I disagree with the premise of trying to create a spectrum of syntax between normal properties and auto-implemented properties. I don't think that normal properties are so verbose as to be a burden on a developer, particularly when you need additional logic.

@lachbaer lachbaer changed the title Proposal: Semi-auto-properties with 'value' keyword Proposal: Semi-auto-properties with setters [topic has evolved] Feb 16, 2016
@lachbaer
Copy link
Contributor Author

How can a OnFooSemiPropChanged event be made possible without defining a regular property the long way? Maybe by yielding the value and then immediately returning to the setter method.

After a night of sleep I have to say that yield does not feel right. yield should be for iterators only!

But for semi-auto-properties the signature for set could be changed to void set(T value, ref T field); Then the backing field can be accessed by reference through the field local parameter.

  internal set
    {
        var oldValue = field;
        OnFooSemiPropChanging(oldValue, /* new */ value);
        field = value;
        OnFooSemiPropChanged(oldValue, /* new */ value);
    }

field is a (reserved) keyword, but like let, await, yield, etc. they are allowed as identifier names for locals, so there would be no conflict here.

What still bothers me is, that I'd like to have a the set routine return a value for assignment

  set => (value > 0) ? value : throw new ArgumentOutOfRangeException();

What about an additional put keyword, that can be used instead of set and requires the return value. Signature is T put(T value, ref T field);. put makes it clear that the signature is different and the return value is awaited.

@lachbaer
Copy link
Contributor Author

The longer I think about it, I have to admit that having a put over set does probably not the justify the effort of changes to Roslyn. Though I still like the "look" of it very much, it might also be a bit confusing to programmers, what actually the difference is between the two. And besides it is only for the purpose of semi-auto-properties.

/* "field" is a reference to the underlying synthesized auto-property backing field */
public int Foo {
    get;
    set { field = (value > 0) ? value : throw new ArgumentOutOfRangeException(); }
    /* or */
    set => field = (value > 0) ? value : throw new ArgumentOutOfRangeException();
}

could be rewritten to

public int Foo {
    get;
    put { return (value > 0) ? value : throw new ArgumentOutOfRangeException(); }
    /* or */
    put => (value > 0) ? value : throw new ArgumentOutOfRangeException();
}

That last put => has optically something nice to it. But when you compare

    set { field = (value > 0) ? value : throw new ArgumentOutOfRangeException(); }
    put { return (value > 0) ? value : throw new ArgumentOutOfRangeException(); }

Here there is really not much of a gain - if not even none at all... But this:

    set => field = (value > 0) ? value : throw new ArgumentOutOfRangeException();
    put => (value > 0) ? value : throw new ArgumentOutOfRangeException();

Of course the 2nd with put looks much cleaner, but does just that look justify introducing a new keyword that might cause ambiguity between set and put? I can't tell and leave it to the C# language design team.

The observant reader might have noticed the use of a local called field. That idea, though, I think is very, very good!

In case of a semi-auto-propery the signature of set changes from

    void set(T value);

to

    void set (T value, ref T field);  /* field is a reference to the synthesized backing field */

This would keep syntax changes nearly not recognizable, and so stays in line with the existing C#. But it empowers C# to define setters for lightweight auto-properties, semi-auto-properties.

[ADDENDUM] The public signature must not change, because otherwise you cannot convert an auto-property to a semi-auto-property without recompiling all referencing binaries. So, this must be internally converted to

    private void set (T value, ref T field);  /* field is a reference to the synthesized backing field */
    public void set(T value) => set(value, ref <Foo>k__BackingField);

For private or assembly internal calls the call should go to the extended set method directly.

@lachbaer
Copy link
Contributor Author

@gafter I have implemented that previously described behaviour as a first feature preview in Roslyn and would like to submit a pull request, after I have prepared the branch for it.
The changes were minimal, understanding the Roslyn code was the hard work ;-)

[UPDATE:] There are still some issues that must be fixed (see addendum above). E.g. now a set_XXX method is generated with the call breaking non-optional ref field. Apart from that it works fine :-)

Can anybody shed a light for me on how to achieve the wanted rewriting? Any hints where I could look such things up in the code?

@lachbaer
Copy link
Contributor Author

What do you think about following syntax for quickly defining a backing field?

public string FirstName {
    get _firstName = "";    // _firstName is 'private string _firstName = "";'
    set { _firstName = value ?? "";  }
 }

Scope of the field is property scoped (#850), or class scope as long as #850 is not implemented. Declaration of the field name could be either after the getter or setter, otherwise the property is probably more complex and a shortcut is not needed.

Advantages over the previous suggestion:

  • no field parameter has to be declared
    => no additional 'keywords' or method signatures
  • getter or setter can be part of the semi-auto-property
  • initialisation of the field is possible
  • prossible automatic code rewriting in the IDE to support a full property
  • Bit more verbose syntax, but not much longer than previous suggestion

Disadvantages:

  • Bit more verbose syntax, more characters ;-)
  • Need to come up with a suitable name for the field

@lachbaer
Copy link
Contributor Author

In #850 @bondsbw complained that the last syntax is not concise. I agree somehow - it was just a thought.

I'll try to point out my goal with this proposal and why it seems important to me.

In the beginning the only way to define a lightweight, no intelligence property like this:
(for those, who don't remeber the old times or never got to know them ;-)

private string _firstName = "";
public string FirstName {
  get { return _firstName; }
  set { _firstName = value; }
}

You will notice that _firstName is written down 3 whole times, just to achieve to have simple property. Quite a lot of code for light goal...

That probably lead to code that relies on public class fields ('variables') more than properties as 'public interface'. It was just too much to write, even with code snippets.

Then, probably for that very reason, in C# 3.0 auto-properties were invented. Now the above code shortens to

public string FirstName { get; set; };

Much better, but there was no way to initialize it. The first access to the above property returns null. Also this doesn't allow for quick read-only properties in immutable classes. If you want a property like the one in the first example, still a whole property must be programmed - leading lazy programmes to accept the constraints of auto-properties, and so possible 'dangerous' code.

C# 6 corrects this

public string FirstName { get; } = "";
public string LastName { get; } = "(none)";
public string FullName => $"{FirstName} {LastName}";

public int Age { get; set; } = 0;

Much better, immutable and with initialization.

Now to the reasons of this proposal:
There is no possibility to influence the supplied value. If somebody wants to set a negative age on the sample above neither an exception is thrown nor a correction to a minimum of 0 is done. This lightweight procedure demands a full property like in C# 1.0 . Again it is very probable for our lazy programmer that she implies that nobody will ever probably do so. As it does not make any sense it will (maybe) never happen ;-)

So, the reason why auto-properties were invented and extended with further possibilities were in the first place was to encourage programmers to use them to make their code more stable and reliable, preventing weak spots and points of failure in the applications.

I want to try to find a syntax that fits into the style of C# and that allows writing of lightweight properties. Those are properties that have no explicitly backing field and only a custom setter or custom getter. [There might be reasons to recalculate the value the moment it is accessed].

The following quote from MSDN should still stay true for semi-auto-properties:

Attributes are permitted on auto-implemented properties but obviously not on the backing fields since those are not accessible from your source code. If you must use an attribute on the backing field of a property, just create a regular property.

I cannot emphasize enough that this proposal is mainly about verification setters. If it emits a syntax feature that is short, precise and could do more, I'm fine :-)

@lachbaer
Copy link
Contributor Author

As local functions will come with C# 7 very likely the following solution might be the currently best approach.

public string FirstName {
   get;
   set { field = value ?? ""; }
} = "none";

will rewrite to ('k__' = compiler generated symbols)

private string k__BackingField = "none";
public string FirstName {
  get { return k__BackingField; }
  set { 
    void k__set_value(string value, ref string field) { field = value ?? ""; }
    k__set_value(value, ref k__BackingField);
  }
}

likewise for get {...}, if only set; is stated. Either get; or set; introduce a semi-auto-property.

Rewriting takes place later in the compiler, so this might be a good way?

@lachbaer
Copy link
Contributor Author

One sample for a semi-auto-property with getter:

public string FirstName {
   get => field ?? "";
   set;
} // = null is implicitly initialized

This would avoid accidentially initializing the property with null and returning this invalid value.

Maybe it is a wise idea to make an initialisation mandatory for semi-auto-properties! It ensures that the programmer choses a suitable starting value for the property, that complies with his setter rule, instead on relying on the compilers chosen init value, that might violate it.

@DavidArno
Copy link

C# 6 corrects this

public string FirstName { get; set; } = "";
public string LastName { get; set; } = "(none)";
public string FullName => $"{FirstName} {LastName}";
public int Age { get; set; } = 0;

Much better, immutable and with initialization.

One point, re the above: there is no immutability in what you have declared there. It's a completely mutable set of properties.

@lachbaer
Copy link
Contributor Author

@DavidArno Typo, corrected it in the sample above. Tnx. Any other comments from you on this topic? :-)

@DavidArno
Copy link

@lachbaer,

My thoughts on this topic are the same as for your proposal to allow expression bodies for get and set when both are used. I think the "pit of success" principle should apply to properties and thus, if you are doing anything other than write simple immutable properties:

public string FirstName { get; } = "";
public string LastName { get; } = "(none)";
public string FullName => $"{FirstName} {LastName}";

then you should have to write more code, eg by having an explicit backing field in the case of this proposal. I may be in a minority amongst C# users in this regard, but that's my position and I'm sticking to it :)

@lachbaer
Copy link
Contributor Author

lachbaer commented Mar 2, 2016

I just have another idea in mind, regarding the return value of a setter for semi-auto-properties.

public string Name {
  get;       // indicates (semi-)auto-property
  set => value ?? "(none)";
}
  • So, the expression body => ...; syntax expects a return value that is assigned to the field.
  • A block body { ... } syntax does not expect a return value.
  • In regular properties expression body and block body both don't return a value in the setter.
  • Semi-auto-properties provide the field argument to either body, representing a reference to the underlying backing field.

What doesn't feel right is that set once expects a return value, once doesn't...

@lachbaer
Copy link
Contributor Author

lachbaer commented Aug 10, 2016

In accordance to #13048 I like to update my proposal: [Updated post]

  1. semi-auto-properties can be readonly?
  2. If so the setter should either correct for a valid value or throw an exception
  3. In this case set return expects a return value to assign
public readonly string Name // can only be set in constructor, 
{
    get;
    set return => value ?? "(noname)";
}

readonly in the declaration gives a hint (e.g. if code is collapsed) that this is a readonly property, because the ... { get; } will most likely be not on the same line. I.e. the readonly keyword will be valid for the established readonly properties as well.

set return upgrades the set-Keyword to emit a streamline code in the constructor like
this.Name = [ bound content of the setter ]

@lachbaer
Copy link
Contributor Author

I made up my mind for quite a time now and my (final) suggestion for semi-auto-properties is as follow.

public string Name
{
    get;
    set { field = value ?? "(empty name"); }
} = "(not set)";

or

public string Name
{
    get;
    set => value ?? "(empty name");
} = "(not set)";
  1. I can see no real need for readonly. Inlining code into the constructor during compilation seems to be no easy thing and you can use a private set that's only called from the constructor anyhow. Besides if static locals will be available it is easy to implement one-time-only setter (otherwise it throws or so).
  2. auto-properties always have a get;, so do semi-auto-properties.
  3. semi-auto-properties have a field contextual keyword that references the hidden backup field. This keyword can also be available in ordinary setters, but has no special meaning there.
    I.e. the setter is called indirectly:
// for block bodies:
void set_XXX(T value)
  => SetXXX(value, ref backingField);
private void SetXXX(T value, ref T field) { ... }
  1. In case of an expression-body for set the expression value will always be assigned to the backing field, if it is not void! (for semi-auto-properties only, of course).
    Im my eyes this is intuitive, because where else sould it go? I don't see any weired real life scenario where you will do something else.
// for expression bodies that return other than void
void set_XXX(T value)
  => backingField = SetXXX(value, ref backingField);
private T SetXXX(T value, ref T field) { ... }
  1. As with any other auto-property the backing field can be initialized.

(What should still be resolved is what happens with concurrency)

@lachbaer
Copy link
Contributor Author

In #850 there came up the idea of a field identifier keyword, similar to the proposal directly above, but kind of always present in properties - for getters and setter.

I have to update my previous post now, because this ever existing in getter and setter has problems.

  1. It could break existing code, if somebody already uses the field identifier
  2. How should the compiler easily know whether to create field or not?
  3. It is kind of confusing in style if there once is, once is not a field

Therefore I update my proposal (again ;-) [but it's getting better every time 😄 ]

In the following cases, a field identifier will be created:

public T PropertyOne {
    get;
    set => field = value;
}

public T PropertyTwo {
    get => field;
    set;
}

public T PropertyThree {
    get => field;
    set => field = value;
} = default(T);

In PropertyOne an (semi-)auto-property is defined by get; (as with C#6). There is no other way for the setter to access the backing field as by the field identifier. Disallowed until now, so no existing code will break;

In PropertyTwo an (semi-)auto-property is defined by set; . There is no other way for the getter to access the backing field as by the field identifier. Consistent with existing syntax, just also allows set;, what is currently forbidden together with a getter method. So doesn't break existing code.

In PropertyThree the semi-auto-property is declared by the = assignment at the end. This isn't allowed when manually writing setters and getters and causes an error. Therefore it again doesn't break existing (valid) code. Also it fits into the current syntax and there is no need for API updates. The main issue with this one is, that an analyzer should check if the field identifier is practically used in either getter and/or setter, otherwise an warning should be issued.

PropertyThree is a bit controverse to me. I could imagine that there are real world scenarios, but in that case it might be wiser to declare a seperate backing field, and/or go with #850 instead (C# isn't VB 😉 )

Under the hood all this could be nicely implemented with the new ref locals

    private T <PropertyThree>k__BackingField;
    public T PropertyThree {
        get {
                ref T field = ref this.<PropertyThree>k__BackingField;
                {
                    return field;
                }
        }
        set {
                ref int field = ref this.<PropertyThree>k__BackingField;
                {
                    field = value;
                }
        }
    } = default(T); // initialisation circumvents setter!

I think, this is now a quite complete approach. Other opinions? 😄

@bondsbw
Copy link

bondsbw commented Dec 17, 2016

Makes sense.

I agree that the PropertyThree case is not necessary, though I also wouldn't mind if it exists for consistency.

What happens for cases where field is declared locally within an accessor method? Or when it is declared as a class member? Those already have to happen in existing usages of field so perhaps how we deal with those cases can reduce the likelihood that there will be a conflict in existing code.

@lachbaer
Copy link
Contributor Author

lachbaer commented Dec 17, 2016

@bondsbw
It won't break existing code, as all three cases currently don't compile because of syntax errors. So there cannot be existing code 😉 .

The ref field always behaves like a local in ordinary methods, so it shadows instance members. But again, by now there cannot be working existing code.

@lachbaer
Copy link
Contributor Author

Discussion moved to dotnet/csharplang#140.

@jcouv
Copy link
Member

jcouv commented Oct 22, 2017

Closing issue since discussion moved to csharplang. Thanks

@jcouv jcouv closed this as completed Oct 22, 2017
@jnm2
Copy link
Contributor

jnm2 commented Apr 4, 2020

Using a backing field without naming it was considered in https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-04-01.md

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