代码之家  ›  专栏  ›  技术社区  ›  Metro Smurf

设置只获取属性值的首选方法:构造函数与支持字段

  •  3
  • Metro Smurf  · 技术社区  · 17 年前

    编辑 尽管我已经接受了 David's 回答, Jon's 答案也应该被考虑。

    设置只读(get only)值时首选哪种方法?属性:使用支持字段还是使用构造函数?假设设计是针对一个属性而不是一个字段(将来可能会有一个更新,要求该属性具有一个setter,这将阻止使用字段)。

    举个简单的例子,哪种方法是首选的?如果一个比另一个更受欢迎,为什么?

    选项1(支持字段) :

    class SomeObject
    {
        // logic
    }
    
    class Foo
    {
        private SomeObject _myObject;
        public SomeObject MyObject
        {
            get
            {
                if( _myObject == null )
                {
                    _myObject = new SomeObject();
                }
                return _myObject;
            }
        }
    
        public Foo()
        {
            // logic
        }
    }
    

    方案2(施工单位) :

    class SomeObject
    {
        // logic
    }
    
    class Foo
    {
        public SomeObject MyObject { get; private set; }
    
        public Foo()
        {
            MyObject = new SomeObject();
            // logic 
        }
    }
    
    4 回复  |  直到 16 年前
        1
  •  7
  •   David    17 年前

    它取决于“new someObject();”所需的时间以及getter被调用的可能性。

    如果创建myObject很昂贵,并且不会每次创建foo()的实例时都使用它,那么选项1是一个好主意,这称为惰性初始化。像Google Chrome这样的程序大量使用它来减少启动时间。

    如果您打算每次都创建myObject,并且getter经常被调用,那么您将保存选项2对每个访问的比较。

        2
  •  5
  •   Jon Skeet    17 年前

    在许多情况下,我喜欢使类型不可变。如果可能的话,我喜欢做 适当地 不可变,这意味着完全避免自动实现的属性——否则类型在同一类中仍然是可变的,这感觉像是一个等待发生的错误。

    “正确的”不变性将包括使支持字段为只读,这意味着 在构造函数中设置它…通常从另一个参数初始化它。我发现很少能像您在问题中所做的那样,在没有任何更多信息的情况下,懒洋洋地创建实例。换句话说,这对我来说是一个更常见的模式:

    public class Person
    {
        private readonly string name;
        public string Name { get { return name; } }
    
        public Person(string name)
        {
            this.name = name;
        }
    }
    

    当您有很多属性时,这就变得很难处理了——将它们全部传递到一个构造函数中可能会很烦人。这就是您想要使用builder模式的地方,它有一个用于收集初始化数据的可变类型,然后一个只使用builder的构造函数。或者,C 4中可用的命名参数和可选参数应该使这稍微容易一些。

    为了回到你的确切情况,我通常会写:

    class Foo
    {
        private readonly MyObject myObject;
        public SomeObject MyObject { get { return myObject; } }
    
        public Foo()
        {
            myObject = new MyObject();
            // logic 
        }
    }
    

    也就是说,除非 SomeObject 特别贵。它比在属性中懒散地执行更简单,而且可能需要担心线程问题。

    现在,我一直在假设不变性在这里是一个有用的目标——但是你一直在讨论添加一个setter。我不知道为什么你认为这会排除使用字段,但它肯定不会。例如,你可以将第一段代码的懒惰实例化和这样的setter结合起来:

    class Foo
    {
        private SomeObject _myObject;
        public SomeObject MyObject
        {
            get
            {
                if( _myObject == null )
                {
                    _myObject = new SomeObject();
                }
                return _myObject;
            }
            set
            {
                // Do you want to only replace it if
                // value is non-null? Or if _myObject is null?
                // Whatever logic you want would go here
                _myObject = value;
            }
        }
    
        public Foo()
        {
            // logic
        }
    }
    

    我认为主要的决定应该是:

    • 是否希望类型正确不可变?
    • 是否需要延迟初始化?
    • 您的属性中是否需要其他逻辑?

    如果所有这些问题的答案都是“否”,请使用自动实现的属性。如果第三个答案更改为“是”,则可以稍后将自动实现的属性转换为“普通”属性。

        3
  •  2
  •   Matt Hamilton    17 年前

    我最近对这种方法很偏爱:

    class Foo
    {
        public SomeObject MyObject { get; private set; }
    
        public Foo()
        {
            MyObject = new SomeObject();
        }
    }
    
        4
  •  0
  •   John Kugelman Michael Hodel    17 年前

    就个人而言,我会先进行初始化,但会将初始化移到构造函数中。

    class Foo
    {
        private SomeObject myObject;
    
        public SomeObject MyObject
        {
            get { return myObject; }
        }
    
        public Foo()
        {
            myObject = new SomeObject();
        }
    }
    

    为什么?我个人不使用 private set 因为我必须记住哪些属性有支持变量,哪些属性没有,这意味着在整个类代码中,有些字段使用小写和大写。我想,那些微小的“矛盾”困扰着我的强迫症。

    不过,这只是一个次要的风格偏好。一个六个,另一个六个。我没有任何反对选项2的内容,也不反对 私有集合 成语。

    推荐文章