18

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?

stakx - no longer contributing
  • 83,039
  • 20
  • 168
  • 268
user3199179
  • 271
  • 3
  • 8
  • 1
    Assign the result of `GetAgeFromSomewhere` to a variable in a separate statement beforehand. – Theodoros Chatzigiannakis Apr 18 '15 at 15:09
  • This probelm also is not directly related to dynamic types but initializers. – CSharpie Apr 18 '15 at 15:10
  • 2
    @CSharpie Using a statically declared type, the OP could have just made `IsLegal` a derivative property: `public bool IsLegal { get { return Age > 18; } }` – Theodoros Chatzigiannakis Apr 18 '15 at 15:12
  • 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? – user3199179 Apr 18 '15 at 15:14
  • 1
    What is your "real scenario"? You may want to explain in your question when / why you would need this – Alex Apr 18 '15 at 15:19
  • 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](https://msdn.microsoft.com/en-us/library/bb397696.aspx), 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). – stakx - no longer contributing Apr 18 '15 at 15:31
  • @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. :) – stakx - no longer contributing Apr 18 '15 at 15:38

5 Answers5

13

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
};
Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
Ron Beyer
  • 11,003
  • 1
  • 19
  • 37
3

Don't complicate thing, keep it simple

//Create a variable
var age = GetAgeFromSomewhere(id);
var profile = new {
  Age = age,
  IsLegal = age>18
}
Satpal
  • 132,252
  • 13
  • 159
  • 168
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);
Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • Since you mention the word "lazy" in your second code block, why not simplify your code using `Lazy`? `private readonly Lazy age = new Lazy(() => GetAgeFromSomewhere(_id)); public int Age { get { return age.Value; } }`. – stakx - no longer contributing Apr 18 '15 at 15:27
  • 1
    I added a statement on object initializers and anonymous types, as well as a usage example of `Lazy`. 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 { ... }`. – Olivier Jacot-Descombes Apr 18 '15 at 15:42
  • 1
    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`!) – Olivier Jacot-Descombes Apr 19 '15 at 17:57
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;
Tom Bond
  • 1
  • 1
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)
};
rittergig
  • 715
  • 1
  • 5
  • 16