【问题标题】:Can properties inside an object initializer reference each other?对象初始值设定项中的属性可以相互引用吗?
【发布时间】:2015-06-25 11:26:03
【问题描述】:

在创建动态对象匿名类型对象(即在对象初始化程序内部)期间,属性是否可能以某种方式相互引用?下面我的简化示例需要重用Age 属性,而无需再次大量调用GetAgeFromSomewhere()。当然是行不通的。关于如何完成此任务的任何建议?

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

对于动态对象匿名类型对象初始化器,这样的事情是否可能?

【问题讨论】:

  • 事先将GetAgeFromSomewhere的结果赋值给单独语句中的变量。
  • 这个问题也不直接与动态类型相关,而是与初始化器相关。
  • @CSharpie 使用静态声明的类型,OP 可以将 IsLegal 设为派生属性:public bool IsLegal { get { return Age > 18; } }
  • 意识到要为值创建一个变量,但我想避免在实际场景中创建十几个变量。还有其他想法吗?
  • 你的“真实场景”是什么?您可能想在您的问题中解释何时/为什么需要这个

标签: c# properties object-initializers


【解决方案1】:

不幸的是,这是不可能的,即使是显式类型的对象。这是因为对象初始化器的工作方式。例如:

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

在“IsLegal”处产生此编译器错误:

错误 1 ​​字段初始值设定项无法引用非静态字段, 方法或属性 'MyClass.Age' ...

字段初始化器不能引用其他非静态字段,并且由于匿名类型不会创建静态字段,因此您不能使用一个字段的值来初始化另一个字段。解决这个问题的唯一方法是在匿名类型之外声明变量并在初始化程序中使用它们。

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

【讨论】:

    【解决方案2】:

    你想要的在对象初始化器中是不可能的。您无法读取正在初始化的对象的属性。 (不管类型是不是匿名的。)

    相反,创建一个类

    public class Profile
    {
        public Profile(int id)
        {
            Age = GetAgeFromSomewhere(id);
        }
    
        public int Age { get; private set; }
        public int IsLegal { get { return Age > 18; } }
    }
    

    或者以偷懒的方式获取年龄:

    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; } }
    }
    

    或使用 Lazy<T> 类(从 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; } }
    }
    

    这样称呼

    var profile = new Profile(id);
    

    【讨论】:

    • 既然您在第二个代码块中提到了“懒惰”这个词,为什么不使用Lazy&lt;int&gt; 简化您的代码呢? private readonly Lazy&lt;int&gt; age = new Lazy&lt;int&gt;(() =&gt; GetAgeFromSomewhere(_id)); public int Age { get { return age.Value; } }.
    • 我添加了关于对象初始化器和匿名类型的语句,以及Lazy&lt;T&gt; 的使用示例。请注意,C# 有一个关键字 dynamic 用于声明动态对象,在另一个上下文中使用该术语可能会造成混淆。在这里,您正在谈论匿名类型new { ... }
    • 请注意,使用new { ... } 创建的类型是匿名的,即它没有公共名称(它有一个编译器知道的内部名称);但是,它是在编译时(而不是在运行时)创建的,因此是静态的,而不是动态的。另请注意,以这种方式创建的对象是强类型的,即在编译时已知具有这种非常匿名的类型,并且该类型的每个属性也是静态类型的。 (如果将dynamic 类型的表达式分配给它的一个属性,则该属性将静态类型为dynamic!)
    【解决方案3】:

    不要把事情复杂化,保持简单

    //Create a variable
    var age = GetAgeFromSomewhere(id);
    var profile = new {
      Age = age,
      IsLegal = age>18
    }
    

    【讨论】:

    • 谢谢,但如果可能的话,在我的真实场景中不创建十几个变量的情况下寻找另一种解决方案
    • @user3199179,然后创建一个普通类并声明派生属性。
    • 我在问动态对象
    • 不同意比问题示例更简单的说法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-27
    • 1970-01-01
    • 2019-09-23
    相关资源
    最近更新 更多