Why doesn't name exist in the current context of shorthand member initialisation?
Asked Answered
I

4

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?

Ingenious answered 20/7, 2017 at 10:6 Comment(14)
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).Wrath
Who downvoted? it's a valid question, why the compiler can't handle thisSlider
Put simply, I guess the answer is: because you can only use the object initializer to initialize variablesEncomiast
In future, please provide a minimal reproducible example - 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.Tiddly
@Fabiano: While I haven't downvoted, it would be considerably better with a minimal reproducible example and a separation of "working" vs "non-working" cases rather than showing them both in the same object initializer.Tiddly
I think I was unclear, c is a property of s, x is a property of cIngenious
And that's why posting a minimal reproducible example 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...)Tiddly
@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 thoughIngenious
@Ingenious It's not complete because we do not know what the st and ct class definitions are.Adulteress
@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...)Tiddly
@Adulteress You are correct. I have edited to add classesIngenious
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.Tiddly
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)Tiddly
@JonSkeet Whoops - I was typing too fast :-)Ingenious
T
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.

Tiddly answered 20/7, 2017 at 10:24 Comment(6)
What you shown is what op already doing. The way he is trying to initialize can't be done in object initializerTalkathon
@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.Tiddly
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 exampleIngenious
@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.)Tiddly
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!Ingenious
Your socratic badge progress is funny: i.sstatic.net/XixNA.png 49 out of 100 when you have 48 undeleted questions :)Hemophilia
G
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

Gambit answered 20/7, 2017 at 10:12 Comment(0)
T
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.

Tiddly answered 20/7, 2017 at 10:24 Comment(6)
What you shown is what op already doing. The way he is trying to initialize can't be done in object initializerTalkathon
@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.Tiddly
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 exampleIngenious
@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.)Tiddly
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!Ingenious
Your socratic badge progress is funny: i.sstatic.net/XixNA.png 49 out of 100 when you have 48 undeleted questions :)Hemophilia
W
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;
Wrath answered 20/7, 2017 at 10:12 Comment(4)
"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.Tiddly
What makes you think it's only for collection initializers? Did you try the code in my answer?Tiddly
@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.Wrath
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.Tiddly
T
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;
Talkathon answered 20/7, 2017 at 10:10 Comment(1)
It's entirely valid to do it in an object initializer - see my answer.Tiddly

© 2022 - 2024 — McMap. All rights reserved.