Can properties inside an object initializer reference each other?
Asked Answered
K

5

22

Is it somehow possible for properties to reference each other during the creation of a dynamic object an anonymously-typed object (i.e. inside the object initializer)? My simplified example below needs to reuse the Age property without making a second heavy call to GetAgeFromSomewhere(). Of course it doesn't work. Any suggestion on how to accomplish this?

var profile = new {
  Age = GetAgeFromSomewhere(id),
  IsLegal = (Age>18)
};

Is something like this possible or not possible with dynamic objects anonymously-typed object initializers?

Korwun answered 18/4, 2015 at 15:6 Comment(7)
Assign the result of GetAgeFromSomewhere to a variable in a separate statement beforehand.Yordan
This probelm also is not directly related to dynamic types but initializers.Papilloma
@Papilloma Using a statically declared type, the OP could have just made IsLegal a derivative property: public bool IsLegal { get { return Age > 18; } }Yordan
Aware of creating creating a variable for the value, but I want to avoid creating a dozen variables in the real scenario. Any other ideas?Korwun
What is your "real scenario"? You may want to explain in your question when / why you would need thisFrivolity
Re: your usage of the term "dynamic objects": There is nothing dynamic about the objects you're creating. They are simply of an anonymous type, which the compiler generates for you behind the scenes. It's perhaps best not to use the word "dynamic" here, because it's easy to get this mixed up with the dynamic keyword and the DLR (which is something very different).Diminution
@user3199179: It seems we both edited at the same time and our edits collided. Seems my version won out. I hope you're OK with it; otherwise edit again, I'll stay back. :)Diminution
A
15

Unfortunately it's not possible, even with explicitly typed objects. This is because of the way object initializers work. For example:

public class MyClass
{
    public int Age = 10;
    public bool IsLegal = Age > 18;
}

Yields this compiler error at "IsLegal":

Error 1 A field initializer cannot reference the non-static field, method, or property 'MyClass.Age' ...

Field initializer can't reference other non-static fields, and since anonymous types don't create static fields, you can't use the value of one field to initialize another. The only way around this, is to declare the variables outside the anonymous type and use them inside the initializer.

int age = GetAgeFromSomewhere(id);
var profile = new {
  Age = age,
  IsLegal = age > 18
};
Allonym answered 18/4, 2015 at 15:44 Comment(0)
J
3

Don't complicate thing, keep it simple

//Create a variable
var age = GetAgeFromSomewhere(id);
var profile = new {
  Age = age,
  IsLegal = age>18
}
Jehias answered 18/4, 2015 at 15:10 Comment(4)
thanks but looking for another solution if possible without creating a dozen variables in my real scenarioKorwun
@user3199179, Then create a normal class and declare derivative properties.Jehias
I'm asking about dynamic objectsKorwun
Disagree with the claim that is it simpler than the question's example.Dysgenic
C
3

What you want is not possible within object intializers. You cannot read properties of the object being initialized. (It does not matter, whether the type is anonymous or not.)

Instead, Create a class

public class Profile
{
    public Profile(int id)
    {
        Age = GetAgeFromSomewhere(id);
    }

    public int Age { get; private set; }
    public int IsLegal { get { return Age > 18; } }
}

Or getting the age the lazy way:

public class Profile
{
    private readonly int _id;

    public Profile(int id)
    {
        _id = id;
    }

    private int? _age;
    public int Age {
        get {
            if (_age == null) {
                _age = GetAgeFromSomewhere(_id);
            }
            return _age.Value;
        }
    }

    public int IsLegal { get { return Age > 18; } }
}

or using the Lazy<T> class (starting with Framework 4.0):

public class Profile
{
    public Profile(int id)
    {
       // C# captures the `id` in a closure.
        _lazyAge = new Lazy<int>(
            () => GetAgeFromSomewhere(id)
        );
    }

    private Lazy<int> _lazyAge;
    public int Age { get { return _lazyAge.Value; } }

    public int IsLegal { get { return Age > 18; } }
}

Call it like this

var profile = new Profile(id);
Callida answered 18/4, 2015 at 15:21 Comment(3)
Since you mention the word "lazy" in your second code block, why not simplify your code using Lazy<int>? private readonly Lazy<int> age = new Lazy<int>(() => GetAgeFromSomewhere(_id)); public int Age { get { return age.Value; } }.Diminution
I added a statement on object initializers and anonymous types, as well as a usage example of Lazy<T>. Note, C# has a keyword dynamic for the declaration of dynamic objects and using this term in another context can be confusing. Here, you are talking about anonymous types new { ... }.Callida
Note that a type created with new { ... } is anonymous, i.e. it has no public name (it has an internal name known by the compiler so); however, it is created at compile time (not at run time) and is therefore static, not dynamic. Note also that an object created this way is strongly typed, i.e. it is known to have this very anonymous type at compile time and each one of the properties of the type are statically typed as well. (If you assign an expression of type dynamic to one of its properties, this property will be statically typed as dynamic!)Callida
I
0

If you don't want to have unnecessary variable, I suggest you use the current object instead :

var profile = new
{
    Age = GetAgeFromSomewhere(id),
};

profile.IsLegal = profile.Age > 18;
Intentional answered 18/2, 2022 at 18:36 Comment(0)
G
0

You can add an additional variable and assign the value inline:

int age; // note: you should NOT set an initial value
var profile = new {
  Age = age = GetAgeFromSomewhere(id),
  IsLegal = (age>18)
};
Georgeanngeorgeanna answered 20/3, 2023 at 7:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.