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

Init only proposal document #3367

Merged
merged 18 commits into from
Apr 21, 2020
Merged

Init only proposal document #3367

merged 18 commits into from
Apr 21, 2020

Conversation

jaredpar
Copy link
Member

No description provided.

Finished up the section detailing using attributes vs. modreq. Decided
to make it an open question for now vs. a consideration. I felt less
strongly about it after writing it. I still do feel quite passionate
though about this with validators.
Got the basic summary and motivation added. Feeling good about the
premise here.
This ended up persuading me that `init` should be on the `set` method,
not the property itself. There is just too much in common with the
`readonly` modifier here. Plus if we ever decide to include the concept
of `init` members as a general feature then `init` would be required to
be on the `set`.
After discussion in LDM we've decide against this as a feature. Reasons
captured in the document.
This updates to the following two LDM decisions:
1. Disallow `init` on fields
1. Use `init` instead of `init set`
proposals/init.md Outdated Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
These mechanisms are effective at allowing the construction of immutable data
but they do so by adding cost to the boiler plate code of types and opting
such types out of features like object and collection initializers. This means
developers must choose between easy of use and immutability.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider being more concrete about "Ease of use". You haven't mentioned adding a set yet.

proposals/init.md Outdated Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
@jaredpar
Copy link
Member Author

Thanks for the feedback @333fred. Got through most of it but still have a bit left. Pushed updates for all the parts I got through.

Finish up the PR feedback on the proposal
@jaredpar
Copy link
Member Author

@333fred responded to all feedback now. One comment is still open for feedback from you but everything else I've cleaned up. Think I took pretty much all your suggestions. 😄

Copy link

@daniel-white daniel-white left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome. Looking forward to this feature eventually landing in the future

proposals/init.md Outdated Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
int Prop2
{
get => 42;
set
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be init right? Otherwise I don't get why those assignments would be valid.

proposals/init.md Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
**Resolution**
This scenario is not seen as compelling by LDM.

### Modreqs vs. attributes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have a link that explains what a modreq is? This is a new term for me personally.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it's a term very few understand. Probably only people who've had to deal with metadata encoding / decoding.

https://twitter.com/jaredpar/status/1247014490069020672

I don't have a good link to a document describing what a modreq or modopt is other than some obscure passages in the ECMA CLI document (here). Should probably write something up about it at some point.

Probably the easiest way to think about them is they're like attributes on method signatures but they're actually a part of the signature. That its an attribute that must be repeated in the same position whenever a method is overridden or when a method implements an interface members. They have the further requirement than if a compiler doesn't understand the type inside a modreq then it should effectively ignore the containing member altogether.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks for the explanation! So basically custom markers for the compiler and the runtime to handle stuff in a special way. Works for me :D
One final question: What does the “mod” in “modreq” and “modopt” stand for? Req is likely requirement and opt is option, but mod? “Modification” as in to make a modification on the generated IL?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Mod" stands for modifier.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More specifically:

  • modreq = modifier required: that is it is required that the compiler reading the entry understand the type in the modreq. That is because the type in the modreq implies special behavior that compilers are required to understand in order to handle the function correctly. Hence if the compiler does not understand it should discard that member.
  • modopt = modifier optional: that is the compiler is not required to understand the type in the modopt. The type indicates behavior that can be beneficial to compilers who understand the behavior but knowledge is not required in order to correctly invoke the member.

proposals/init.md Outdated Show resolved Hide resolved
proposals/init.md Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
proposals/init.md Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
@mgravell
Copy link
Member

mgravell commented Apr 20, 2020

Hi; can I request clarification for what features, if any, will be discoverable / usable via reflection? In particular, I'm thinking of meta-programming code that pre-dates generators (serializers, ORMs, etc), and which may do any of the following today:

  • discover properties with manual (non-auto) getters and setters (not necessarily public) and call those via regular reflection during materialization; assume that set becomes an init: will the init be discoverable and callable?
  • discover properties with get-only auto-properties and discover the underlying field via Roslyn implementation knowledge (yes, this happens): assume this becomes get; init; - will the backing field remain the same pattern for the old approach? is the init preferable, if it is discoverable and callable via regular reflection?
  • all of the above with IL-emit in the same runtime?
  • any it may be moot since there is not currently an "emit to disk" API, but are there any concerns re IL-verificatioj for runtime emit users? Would we need to retain the object purely on the stack? (which may not work well with complex scenarios), or (if verification is even a concern) would we need the same approach as async?
  • on async - maybe allow marking a field/parameter/[local?] with an attribute (in CompilerServices): any such is considered "under construction" for the purposes of IL-emit? So: the backing field for hoisted async construction would be marked such.

In particular I want to guard against scenarios where a consumer moves from public int Foo {get;private set;} to public int Foo {get;init;}, which looks like it makes it more accessible. Can a suitably updated library still do all the things?

@giuliano-barberi-tf
Copy link

Hi, will init properties also be taken into consideration when initializing NRTs somehow? It would be great if the caller was forced to specify a value for a non-nullable reference type, otherwise I'm guessing you would be required to still initialize it from the constructor or make init properties always nullable?

Copy link
Contributor

@tiagocesar tiagocesar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some typos :)

proposals/init.md Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
@mgravell
Copy link
Member

mgravell commented Apr 20, 2020

One of the main pivot points in how this feature is encoded will come down to the following question:

Is it a binary breaking change to replace init with set?

I agree that is a key question (and we should aim for "this doesn't break things", since most folks will just think of this as "making it more accessible, therefore compatible"), but IMO that is the second question, since no init code currently exists. The first question is:

Is it a binary breaking change to replace set with init?

i.e. can I change existing code that may have been fully settable (but perhaps using popsicle immutability internally) to code that expresses the new intent via init? It is understood that his may represent a build-breaking change when used outside of initialization, but is it a runtime breaking change? From the perspective of libraries, the runtime breaking is just as big - perhaps bigger - of a concern. If this is seen as a compelling case, then maybe an attribute is preferable over a modreq. However, in that scenario it may also be desirable to annotate the property automatically with [ReadOnly(true)], which is used by many UI tools (via the PropertyDescriptor API) to determine mutability. Alternatively, of course, the default PropertyDescriptor over PropertyInfo could be updated to detect the new accessor attribute.

proposals/init.md Outdated Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
}
```

Adding `init` to these properties is a non-breaking change:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a perfect lightbulb/analyzer suggestion.

@hexawyz
Copy link

hexawyz commented Apr 20, 2020

My apologies if I missed a previous discussion about this, but I feel slightly unconfortable about the proposed syntax.
Not considering possible future evolutions mentioned in the proposal, in the context of get-only auto-properties, init and get seem somewhat redundant.
i.e. Would init alone be allowed, and what would it mean?

Have you considered a simpler alternative form where init; alone is a syntaxical replacement for get; with the broader meaning of get; init; from the current proposal?

e.g.

// Property is only settable at initialization and in the constructor
public int PropA { init; }
// Property is only settable in the constructor
public int PropB { get; }
// Property is settable everywhere
public int PropC { get; set; }

// Can be read publicly, but only written-to inside the constructor or "internally" during object initialization
public int PropD { internal init; }

@poke
Copy link
Contributor

poke commented Apr 20, 2020

@GoldenCrystal Set-only properties are allowed, so I don’t see why init-only properties, without a getter, shouldn’t be. So no, I wouldn’t say that a getter is implicit when you specify init on auto-properties. This is especially true because the get and init may have different access modifiers on them which makes the distinction between them very important.

@hexawyz
Copy link

hexawyz commented Apr 20, 2020

Set-only properties are allowed

Sure, set-only properties are allowed by the language spec, but:

  • Their use is heavily discouraged: See the official design guidelines that specifically mentions

    ❌ DO NOT provide set-only properties or properties with the setter having broader accessibility than the getter.

    Generally, consider what it means & how bothersome it is to not be able to read back what you just wrote to a property…
    Given that properties are intended to be (mostly 😁) behaving as fields, that doesn't make a lot of sense. (Of course, I can't speak for the language designers, but I think that if they had to make the choice again today, they might not allow set-only properties at all)

  • Set-only auto-properties are not allowed at all. Anyway, what would be the point of having a property that you can write to but absolutely never ever read from? (Except by relying on reflection APIs of course)

If anything, I'd even argue that R/W auto-properties should be allowed to be declared as simply T P { set; } insetad of current T P { get; set; }, but that might a a bit off-topic here 😅

This is especially true because the get and init may have different access modifiers on them which makes the distinction between them very important.

I addressed the idea of a different accessibility for implicit get and (explicit) init in the code sample above. 🙂 (Not saying it's perfect, though)

@jaredpar
Copy link
Member Author

@mgravell

Hi; can I request clarification for what features, if any, will be discoverable / usable via reflection?

will the init be discoverable and callable?

Yes. The .NET API review notes for the IsInitOnly member haven't been published yet. But one particular discussion point was making sure that init members were discoverable via the reflection API. At the moment there will be no simple helper like IsInitOnly on the accessor but we made sure that a determined user could find the encoding via the existing APIs.

Whether or not to provide the more friendly APIs is a bit of a debate point right now. The upside is that it makes it easier to write meta programming tools but the downside is it has a tendency to veer reflection from being language neutral to being more C# centric.

will the backing field remain the same pattern for the old approach?

Yes there will be no change to how the backing field is encoded here.

all of the above with IL-emit in the same runtime?

Yes

any it may be moot since there is not currently an "emit to disk" API, but are there any concerns re IL-verificatioj for runtime emit users?

At the moment .NET Core doesn't have an IL verification story hence there is nothing to worry about right now. I did add a section in the document about what IL verification would look like for this feature but until we have an actual implementation and updated spec it's a bit speculative.

on async - maybe allow marking a field/parameter/[local?] with an attribute (in CompilerServices): any such is considered "under construction" for the purposes of IL-emit? S

If we every get an IL verification story with .NET Core then I think this will become a necessity for us. Until we have that though I suspect we won't emit the hoisted fields in any special way. It would make state machines bigger without providing any value. Or at least from the perspective of the C# language there is no benifit from doing this. Is there a meta programming consumption story that I'm missing here?

Lots of typos 😄

Co-Authored-By: Tiago César Oliveira <[email protected]>
Co-Authored-By: Patrick Westerhoff <[email protected]>
Co-Authored-By: Steve Ognibene <[email protected]>
Co-Authored-By: Viacheslav Ivanov <[email protected]>
proposals/init.md Outdated Show resolved Hide resolved
proposals/init.md Show resolved Hide resolved
proposals/init.md Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
@333fred
Copy link
Member

333fred commented Apr 20, 2020

Not considering possible future evolutions mentioned in the proposal, in the context of get-only auto-properties, init and get seem somewhat redundant.

@GoldenCrystal you seem to be approaching this in exactly the opposite way from what we did in LDM, which is interesting to me. You propose init as a modifier to get, when what we view it as is a modifier to set. Specifically, it is a setter that has had it's valid scope restricted to a single location. In particular, your last example of differing accessibilities shows why that is a no-go in my book: there's no way to define an internal-only getter.

proposals/init.md Outdated Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
proposals/init.md Outdated Show resolved Hide resolved
Co-Authored-By: Julien Couvreur <[email protected]>
Co-Authored-By: Fred Silberberg <[email protected]>
@hexawyz
Copy link

hexawyz commented Apr 21, 2020

you seem to be approaching this in exactly the opposite way from what we did in LDM, which is interesting to me. You propose init as a modifier to get, when what we view it as is a modifier to set.

@333fred
Sorry about that 😄
I read the proposal mostly with readonly structs and get-only auto-properties in mind, as I thought that this was where the changes were the most interesting.
I do nevertheless understand that you're trying to implement a feature set that is coherent between auto and manual properties, and that init technically needs to be a setter.
(Please do not read my comments as trying to diminish the value of this proposal; I do think it is great overall 😊)

Not sure if I need to detail it, but my reasoning was the following:
Today, a property int P { get; } is already writable (from the constructor), despite only being indicated as get.
While maybe not technically true, we could very well consider this behavior to be entirely supported by get;, as opposed to get {} or get =>: Since set; also include the ability to write to the property from the constructor, this assertion cannot be observed to be false. (So it may as well be true 🤷‍♂️)

The only thing we currently miss from get-only auto-properties is the ability to be written from object initializers in addition to the type constructors, which this proposal intends to adress (hopefully on the way to C# records ?)

With that in mind, when I see public int A { get; init; }, I instinctively read it as init; being some kind of weird modifier that would mean "And you can also write it from object initializers"… And the funny thing is that, conceptually, this is mostly what it does.

Also, I do not know how to word it properly, but I found the { get; init; } syntax to be a bit clunky. Adding 6 extra characters (5 without spaces) per property for a feature we'd like to have by default most of the time seems a bit much.
I tend to think that initializable auto-properties will end up being used the most, so I'd really like the syntax to be optimized around it.
Hence, since init; alone wouldn't mean anything, that it extends get;, and that get already has a slightly different meaning in case of auto-properties, why not just omit get; and use only init;? (The same being true for set; but get; set; is already there 😕)

In particular, your last example of differing accessibilities shows why that is a no-go in my book: there's no way to define an internal-only getter.

I think this is solved nicely if we consider that specifying the get accessor is optional when it can be infered. (That was my backup proposal anyway… 😁)

In the end, as far as auto-properties are concerned, we could see things in two different, yet not necessarily exclusive, ways:

  • In terms of cabability, getinitset
  • get is mandatory in C# 8 and below; but it could as well be rendered optional. We only strictly need it to differentiate between a field and a get-only auto-property.

So, hmm…
Especially in the context of adding a different kind of property setter, could we make the get; optional in auto-properties?

That'd be a related, but totally different proposal, so I can open a separate issue to discuss this

@333fred
Copy link
Member

333fred commented Apr 21, 2020

In terms of cabability, get ⊂ init ⊂ set

See, that's actually not true at all. get and set are entirely orthogonal here: You can have set-only properties that cannot be read. We advise against creating them, true, but you can have them. It's an important point, though: nowhere in C# today do we have a mutability accessor or modifier that implies the member it is applied to is also readable as well as writable. If we were to allow init on its own, this would change; we'd have a thing, that changes where a member is able to be assigned (be an lvalue) also imply that it can be read (be an rvalue). There's also a matter of optics: when I'm reading the code, the word init does not imply anything about the readability of the code. Yes, you might say that readonly or a single get does not imply this either, but those are well-established patterns in the language and intuitively make sense, as something needs to set an initial value.

@jaredpar
Copy link
Member Author

Thanks for all the feedback!

@jaredpar jaredpar merged commit 3f177e9 into dotnet:master Apr 21, 2020
@jaredpar jaredpar deleted the init branch April 21, 2020 03:52
Comment on lines +141 to +146
init
{
Field1 = 13; // okay
Field2 = 13; // okay
Prop1 = 13; // okay
}
Copy link

@Carl-Hugo Carl-Hugo Apr 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you want to add that, and what would be the advantage? To me, this is duplicating the job of a constructor, and I think this will only create less readable code. Maybe there is a use-case that I missed; if so, feel free to enlighten me.

Edit: oops, just noticed that you merged the PR 15h ago (the tab was open since yesterday); well my comment applies nonetheless

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the motivation section of this proposal adequately outlines our reasoning: we want to support nominal, immutable construction patterns which we do not do today.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the motivation section of this proposal adequately outlines our reasoning: we want to support nominal, immutable construction patterns which we do not do today.

Yes, I get the intent @333fred , which is a really good idea; less boilerplate is always welcome. I still remember the day you added auto-implemented properties; what a day!

But what is the difference between the following two blocks of code?

init
{
    Field1 = 13; // okay
    Field2 = 13; // okay
    Prop1 = 13; // okay
}
public SomeCtor()
{
    Field1 = 13; // okay
    Field2 = 13; // okay
    Prop1 = 13; // okay
}

Then what is the added value of that new way to initialize fields/props? (for C# users)

An intend is one thing, but does that help solve a problem or does it only create noise and possible confusion? Think about it, people will have one more place to put initialization logic in, does someone need that? If yes, who does? What use-case does this solve that was unsolvable before or that required more boilerplate?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think about optional parameters in a constructor today: you have a location that can default them if not provided, or throw if a combination of provided parameters is invalid. This adds the same logic for properties that are inited by the caller, but then immutable afterwards.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think about optional parameters in a constructor today: you have a location that can default them if not provided, or throw if a combination of provided parameters is invalid. This adds the same logic for properties that are inited by the caller, but then immutable afterwards.

When I think about that I see types that could require some kind of possible value/props/null/not null combo but without any way to know it in advance from the API of that class/struct. People can be creative 😉

Then what about I add another property that initializes init and readonly props/fields as well?

class Complex
{
    readonly int Field1;
    int Field2;
    int Prop1 { get; init ; }

    int Prop2
    {
        get => 42;
        init
        {
            Field1 = 13; // okay
            Field2 = 13; // okay
            Prop1 = 13; // okay
        }
    }

    int Prop3
    {
        get => 123;
        init
        {
            Field1 = 3;
            Field2 = 3;
            Prop1 = 3;
        }
    }
}

In what order are those going to be initialized?

Wouldn't it be clearer to do the following?

class Complex
{
    readonly int Field1;
    int Field2;
    int Prop1 { get; init ; }
    int Prop2 { get; init; }
    int Prop3 { get; init; }

    public Complex()
    {
        Field1 = 13;
        Field2 = 13;
        Prop1 = 13;
        Prop2 = 42;
        Prop3 = 123;
    }
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn't necessarily take syntax examples from a proposal that merely illustrate what things are legal where as a good API design. More than likely, most init properties would initialize one, and only one, readonly field. You can still write your example today, using set instead of init, and it provides a markedly worse experience if you wanted it to actually be an immutable object.

As to ordering, that's the thing here: this is about nominal object creation. Nominal creation is unordered, that's the whole point. If you want to control the order in which properties are set, then you need to use the existing tools we have today in positional creation (namely, standard constructor arguments).

Imagine you had an example like this:

public class PathObject
{
    string ActualPath { get; init; }
    bool SupportsLongPath { get; init; }
    init
    {
        if (ActualPath == null || !supportsLongPath && ActualPath.Length >= 256)
        {
            throw new ArgumentException(nameof(ActualPath), $"Long path support not enabled, but path is {ActualPath.Length}!");
        }
    }
}

Imagine if you had to write that without init support? You literally can't do it. There's no way today to ensure that ActualPath was initialized by the caller. Further, for validation of the length of ActualPath, you can't do that either: you need to ensure that SupportsLongPath is set before attempting to validate the path length. The only way to handle that correctly would be to use a constructor, which guarantees ordering. However, that brings burdens of its own. Let's say that this is a public class in a library that you publish publicly. You decide that you want to add support for hidden path object creation. But there's a problem: you've already published this class. If you want to avoid breaking your customers, you're going to have to maintain this 2-parameter constructor forever more, because removing it would break existing customer binaries. In the init world, this pain point is gone. You add a new init-only property, validate as necessary in the init body, and no one who compiled against a previous version of your library has to care about this new optional parameter that you've added. For a very real example of this type of pain, look at https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/CSharpCompilationOptions.cs#L787-L913. We cannot remove these constructors, as existing binaries depend on them, even the constructor marked as Bad - DO NOT USE. Those constructors will never be able to go away, unfortunately, but at least we won't have to add more of them.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I could write my messy code in a set, so init is similar in that way (well, its an "initializable readonly property setter" after all).

That said, I did not see the "init constructor-like" that you used in your last sample anywhere in the proposal, maybe you should add that as it seems that will be needed.

If the initialization is unordered, how can you be sure that your constructor-like-init code block will be hit after the init setter of the other properties?

More on that, to make the usage clearer for users, maybe afterinit would be easier to understand for non-compiler/csharplang-guru? It would also point people in the direction of centralizing initialization logic instead of splitting it everywhere. Maybe a beforeinit could benefit centralizing initialization while afterinit benefits validation?

Or to make it less "magic" and more discoverable, what about an IInitializable interface that adds a void AfterInit(); and a void BeforeInit() methods usable during the initialization process? Same idea here, you need additional logic, you implement the interface (or create the afterinit block) and code your initialization logic there; otherwise, you don't pollute your code with it.

As I understand it, you seem to want to help people get into C#, so making usage and understanding of new (and existing) features as easy as possible for everyone would probably help that. Just for fun, ask people in what order are executed auto-property initializers and constructors to see how unclear it can be. With init you will be adding another layer of "confusion" over that.

As for breaking changes, a new major version should allow you to clean stuff up and introduce breaking changes. Everyone makes mistakes but having to carry all of the mistakes of everyone forever ain't helping anyone 😉

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That said, I did not see the "init constructor-like" that you used in your last sample anywhere in the proposal, maybe you should add that as it seems that will be needed.

It'll be coming in a separate proposal. It'll all part of a greater feature unity we're aiming for as part of records.

If the initialization is unordered, how can you be sure that your constructor-like-init code block will be hit after the init setter of the other properties?

The compiler will be required to generate a call to the validator (straw-man name we're using for now) after an object initializer is run, so ordering won't matter.

Don't take the syntax I used as "How it will look exactly". A future proposal with more validator design will be coming, and we haven't talked much about the specific syntax beyond "We like the general idea".

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to know, thanks, I'll try to follow that up then.

The decision was made to move forward with `init` as a standalone accessor in
the property accessor list.

### Warn on failed init

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any plans to add some option or another keyword that would force property to be set in initializer or constructor?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've touched on that topic in recent discussions, but haven't made any plans for that yet.

@YairHalberstadt
Copy link
Contributor

Will you be able to set an init only property from a nested object initializer?
Not to allow that would be inconsistent.

Allowing it would let you to set an initonly property of any arbitrary object:

public class Foo
{
    public int Bar { get; init; }
}
public struct InitSetter<T>
{
    public InitSetter(T t) => Value = t;
    public T  Value { get; }
}

...

public void M(Foo foo)
{
     new InitSetter<Foo>(foo)
     {
          Value = 
          {
              Bar = 42,
          }
     }
}

@alrz
Copy link
Member

alrz commented May 6, 2020

Will you be able to set an init only property from a nested object initializer?

At that point the object is already created. So it's not really in the initialization context. I'd expect not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.