9

I am using an object initializer for a st object:

public class Container 
{
    public Container () { ContainedItem = new Item; }
    public Item ContainedItem { get; set; }
}

public class Item
{
    public string Value { get; set; }
}

var MyContainer = new Container()
{
    // I want to populate the the property Value of the property Item
    // with the second line rather than the first
    ContainedItem = new Item() { Value = FooString }, // This works
    ContainedItem.Value = FooString  // This assigns to the same member but does not work
};

The second initializer line gives the error:

The name 'ContainedItem' does not exist in the current context.

Invalid initializer member declarator.

and suggests declaring ContainedItem somewhere in local scope.

Now as the first line works it can be seen that ContainedItem is in fact a valid property of Container and that MyContainer.ContainedItem is definitely not null... so why does the following line fail to recognise it?

Toby
  • 9,696
  • 16
  • 68
  • 132
  • You can´t initialize a member within an initializer *and* call any of its members (at least not on the left side of an assignemtn). – MakePeaceGreatAgain Jul 20 '17 at 10:10
  • 1
    Who downvoted? it's a valid question, why the compiler can't handle this – Fabiano Jul 20 '17 at 10:10
  • Put simply, I guess the answer is: because you can only use the object initializer to initialize variables – Steven Lemmens Jul 20 '17 at 10:17
  • In future, please provide a [mcve] - it would be easier to understand if you showed the types involved, and gave them conventional names. You also seem to have elided two cases. – Jon Skeet Jul 20 '17 at 10:20
  • @Fabiano: While I haven't downvoted, it would be *considerably* better with a [mcve] and a separation of "working" vs "non-working" cases rather than showing them both in the same object initializer. – Jon Skeet Jul 20 '17 at 10:21
  • I think I was unclear, `c` is a property of `s`, `x` is a property of `c` – Toby Jul 20 '17 at 10:25
  • And that's why posting a [mcve] rather than an example using unconventionally-named types that you haven't shown us isn't a great idea. (See my answer for a complete example that *does* show the types...) – Jon Skeet Jul 20 '17 at 10:26
  • @JonSkeet this is the MCVP - if I have this exact code (with *both* lines) I see the mentioned error on the second line. Apologies for the unconventional names though – Toby Jul 20 '17 at 10:26
  • @Toby It's not **complete** because we do not know what the `st` and `ct` class definitions are. – Adwaenyth Jul 20 '17 at 10:27
  • @Toby: In what way is it complete, when you haven't provided `st` or `ct`? Including both lines like that makes it unclear whether that's what you *want* or whether you only want the second line, on its own. Fundamentally, you're simply trying syntax that doesn't exist, but it would be easier to help you if you were clearer about what you were trying to achieve. (If you *do* want to initialize `c` so a specific value and modify that value, then just use an object initializer for that as you've already shown you can do...) – Jon Skeet Jul 20 '17 at 10:28
  • @Adwaenyth You are correct. I have edited to add classes – Toby Jul 20 '17 at 10:30
  • It's still unclear what you're *trying* to achieve. Do you want to not have to *set* the `Item` property at all, or are you trying to set it and then modify it in a second line? If it's the former, my answer shows what you want. If it's the latter, I fail to see why you'd want to do that a separate item in the object initializer when you can do it in the object initializer for `new Item`. – Jon Skeet Jul 20 '17 at 10:38
  • Btw, this still isn't a verifiable example as it won't compile at a place *unrelated* to the question: `Item = new Item;` isn't valid, and you don't have a `;` after the declaration of `Value`. It's not clear why you've taken my *valid* example and made it invalid (and introduced public fields instead of properties at the same time... urgh) – Jon Skeet Jul 20 '17 at 10:39
  • @JonSkeet Whoops - I was typing too fast :-) – Toby Jul 20 '17 at 10:44

4 Answers4

4

The syntax for inline object initialisation is well specified. You can't just make stuff up and expect the compiler to understand. The syntax is strictly:

{
    Property1 = Value1,
    Property2 = Value2,
    ...
}

c.x is not a property of st. c is. Hence, you cannot say c.x = Bar[i].x

From the C# Language Specification section 7.6.10.2:

object-initializer:

{ member-initializer-listopt }
 { member-initializer-list , } member-initializer-list:
 member-initializer
 member-initializer-list , member-initializer member-initializer:
 identifier = initializer-value initializer-value:
 expression
 object-or-collection-initializer

Sweeper
  • 213,210
  • 22
  • 193
  • 313
3

You can assign values to "sub-properties" as it were, just not with that syntax. Here's a complete example:

using System;

public class Container
{
    public Item Item { get; set; } = new Item();
}

public class Item
{
    public string Value { get; set; }
}

class Test
{
    static void Main(string[] args)
    {
        var container = new Container
        {
            Item = { Value = "hello" }
        };
    }
}

The object initializer in Main is equivalent to:

var tmp = new Container();
tmp.Item.Value = "hello";
var container = tmp;

Note that this relies on Container.Item returning a valid object without it having been explicitly initialized in the object initializer - which is the case in my example, but isn't always the case.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • What you shown is what op already doing. The way he is trying to initialize can't be done in object initializer – Rahul Jul 20 '17 at 10:35
  • @Rahul: Where has the OP shown the use of the `Item = { Value = "hello" }` syntax? That's what I *think* they're trying to do with their second line, not wanting to need the first line - in which case this satisfies their needs. The question could be clearer though. – Jon Skeet Jul 20 '17 at 10:36
  • Sorry for the lack of clarity. I have hopefully improved this some with my edits. Yes I would like to not need the sub-initializer on the first line, I would like to need only the second line. but only the first line works, even when used together as in my example – Toby Jul 20 '17 at 10:38
  • @Toby: I fail to understand how my answer *doesn't* satisfy your question then. It assigns a value to the `Value` property of the item, without having to set `Item` explicitly. (The `Item` setter is never called in my example.) – Jon Skeet Jul 20 '17 at 10:40
  • Ah I have the syntax for sub properties mixed up! I just followed the idea of chaining with '.' when I should have used braces as per your code above - many thanks! – Toby Jul 20 '17 at 10:47
  • Your socratic badge progress is funny: https://i.stack.imgur.com/XixNA.png 49 out of 100 when you have 48 undeleted questions :) – fedorqui Jul 20 '17 at 11:19
1

You can´t use an expression like c.x on the left side of an assignement within an initializer. This includes methods-calls as well as getters/setters:

var s = new S { x.MyMethod() };

The only thing you can do in an intializer is to set a property of the current type.

From MSDN:

Object initializers let you assign values to any accessible fields or properties of an object

However c.x is not a field or property of st, it´s not even a valid name.

That´ll work however:

var s = new st();
{
    c = new ct()
};
s.c.x = Bar[i].x;
MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
  • "The only thing you can do in an initializer is to set a property of the current type." Not true. See my answer, which *gets* a property from the current type, in order to set a property of that "nested" value. – Jon Skeet Jul 20 '17 at 10:29
  • What makes you think it's only for collection initializers? Did you try the code in my answer? – Jon Skeet Jul 20 '17 at 10:42
  • @JonSkeet Indeed, I misread your answer and whrongly thought `Item` was a collection. Anyway I don´t have C#6 to test this. But I´m interested if calling `Item = { Value }` will only call the getter or also the setter. – MakePeaceGreatAgain Jul 20 '17 at 10:46
  • As I said in my answer, it only calls the getter. You don't need C# 6 to test this - it's been available since C# 3. – Jon Skeet Jul 20 '17 at 11:28
0

That's cause you are trying to do inside object initializer which is for initializing the object members and c.x isn't a defined member of object s. Thus the said error. Rather try doing it outside of object initializer saying

s.c.x = Bar[i].x;
Rahul
  • 76,197
  • 13
  • 71
  • 125