【问题标题】:Difference between Property and Field in C# 3.0+C# 3.0+ 中属性和字段的区别
【发布时间】:2010-10-13 19:17:21
【问题描述】:

我意识到它似乎与What is the difference between a Field and a Property in C#? 重复,但我的问题略有不同(从我的角度来看):

一旦我知道了

  • 我不会将我的课程与“仅适用于属性的技术”一起使用,并且
  • 我不会在 getter/setter 中使用验证码。

有什么不同(除了样式/未来开发的),比如设置属性的某种类型的控制吗?

是否有任何额外的区别:

public string MyString { get; set; }

public string myString;

(我知道,第一个版本需要 C# 3.0 或更高版本,并且编译器确实会创建私有字段。)

【问题讨论】:

标签: c# properties c#-3.0 field automatic-properties


【解决方案1】:

字段和属性看起来相同,但实际上并非如此。属性是方法,因此属性不支持某些事情,并且某些事情可能会发生在属性上,但不会发生在字段的情况下。

以下是差异列表:

  • 字段可用作out/ref 参数的输入。属性不能。
  • 一个字段在多次调用时总是会产生相同的结果(如果我们忽略了多个线程的问题)。 DateTime.Now 这样的属性并不总是等于它自己。
  • 属性可能会引发异常 - 字段永远不会这样做。
  • 属性可能有副作用或需要很长时间才能执行。字段没有副作用,并且对于给定类型总是尽可能快。
  • 属性支持 getter/setter 的不同可访问性 - 字段不支持(但可以将字段设置为 readonly
  • 使用反射时,属性和字段被视为不同的MemberTypes,因此它们的位置不同(例如GetFieldsGetProperties
  • 与字段访问相比,JIT 编译器可能会以非常不同的方式处理属性访问。但是,它可能会编译为相同的本机代码,但存在差异的范围。

【讨论】:

  • 但是请注意,如果采用了良好的做法,这些点中的一些点应该有区别。也就是说,属性真的不应该有副作用,也不应该花费很长时间来执行。
  • @Noldorin:我同意,但不幸的是 should 是这里的关键字。使用字段可以保证行为。我并不是说你应该使用字段,但重要的是要注意语义差异。
  • 是的,很公平。不幸的是,初学者程序员通常对这些事情一无所知...
  • 另外,字段可能有一个字段初始化器,而属性必须在构造函数中初始化。
  • 我觉得这个答案比公认的答案好很多。我开始认为总是更喜欢属性而不是字段的“可接受”方式是不好的想法。如果您只需要处理数据,请使用字段。如果您需要将功能应用于数据,请使用方法。由于属性可能具有您不知道的副作用(尤其是如果您没有设计库并且文档很少),因此在大多数情况下,它们对我来说似乎与直觉相反。
【解决方案2】:

封装。

在第二个实例中,您刚刚定义了一个变量,在第一个实例中,变量周围有一个 getter / setter。因此,如果您决定要在以后验证变量 - 这会容易得多。

此外,它们在 Intellisense 中的显示方式也不同 :)

编辑:更新 OPs 更新问题 - 如果您想忽略此处的其他建议,另一个原因是它根本不是好的 OO 设计。如果你没有很好的理由这样做,总是选择一个属性而不是公共变量/字段。

【讨论】:

  • 为什么会更容易?是什么阻止我将字段转换为属性并添加私有支持字段?这对调用代码有何影响?
  • @Serge - 它会影响已编译的代码。例如,如果您正在开发一个由多个应用程序使用的库,那么将一个字段更改为该库中的一个属性将需要重新编译每个应用程序。如果它是一个属性,您可以放心地更新该属性。
  • 我完全同意你的观点,我总是使用属性。我只是对可能的差异感到好奇
  • 如果使用代码总是与受影响的类同时重新编译(因此任何私有或内部没有内部可见的东西都是 100% 安全的),那么将它作为一个字段是完全可以的
  • 哇 Shuggy 你的评论正是我要找的答案!
【解决方案3】:

几个快速、明显的区别

  1. 属性可以有访问器关键字。

    public string MyString { get; private set; }
    
  2. 可以在后代中覆盖属性。

    public virtual string MyString { get; protected set; }
    

【讨论】:

  • mmh.. nr 2 很有趣.. 我没想到
【解决方案4】:

根本区别在于,字段是内存中存储指定类型数据的位置。一个属性表示一个或两个代码单元,执行这些代码单元以检索或设置指定类型的值。这些访问器方法的使用在语法上是通过使用一个看起来像字段的成员来隐藏的(因为它可以出现在赋值操作的任一侧)。

【讨论】:

    【解决方案5】:

    访问器不仅仅是字段。其他人已经指出了几个重要的区别,我将再添加一个。

    属性参与接口类。例如:

    interface IPerson
    {
        string FirstName { get; set; }
        string LastName { get; set; }
    }
    

    可以通过多种方式满足此接口。例如:

    class Person: IPerson
    {
        private string _name;
        public string FirstName
        {
            get
            {
                return _name ?? string.Empty;
            }
            set
            {
                if (value == null)
                    throw new System.ArgumentNullException("value");
                _name = value;
            }
        }
        ...
    }
    

    在这个实现中,我们保护Person 类不进入无效状态,以及调用者不从未分配的属性中获取空值。

    但我们可以进一步推动设计。例如,接口可能不处理 setter。可以说IPerson 接口的消费者只对获取属性感兴趣,而不是设置它:

    interface IPerson
    {
        string FirstName { get; }
        string LastName { get; }
    }
    

    Person 类的先前实现满足此接口。从消费者(消费IPerson)的角度来看,它让调用者也设置属性的事实毫无意义。具体实现的附加功能由例如builder考虑:

    class PersonBuilder: IPersonBuilder
    {
        IPerson BuildPerson(IContext context)
        {
    
            Person person = new Person();
    
            person.FirstName = context.GetFirstName();
            person.LastName = context.GetLastName();
    
            return person;
    
        }
    }
    
    ...
    
    void Consumer(IPersonBuilder builder, IContext context)
    {
        IPerson person = builder.BuildPerson(context);
        Console.WriteLine("{0} {1}", person.FirstName, person.LastName);
    }
    

    在这段代码中,消费者不知道属性设置器——知道它不是他的事。消费者只需要getter,他从接口获取getter,也就是从合约中获取。

    IPerson 的另一个完全有效的实现是不可变的人员类和相应的人员工厂:

    class Person: IPerson
    {
        public Person(string firstName, string lastName)
        {
    
            if (string.IsNullOrEmpty(firstName) || string.IsNullOrEmpty(lastName))
                throw new System.ArgumentException();
    
            this.FirstName = firstName;
            this.LastName = lastName;
    
        }
    
        public string FirstName { get; private set; }
    
        public string LastName { get; private set; }
    
    }
    
    ...
    
    class PersonFactory: IPersonFactory
    {
        public IPerson CreatePerson(string firstName, string lastName)
        {
            return new Person(firstName, lastName);
        }
    }
    ...
    void Consumer(IPersonFactory factory)
    {
        IPerson person = factory.CreatePerson("John", "Doe");
        Console.WriteLine("{0} {1}", person.FirstName, person.LastName);
    }
    

    在此代码示例中,消费者再次不知道填充属性。 Consumer 只处理 getter 和具体实现(以及它背后的业务逻辑,比如测试 name 是否为空)留给专门的类 - 构建器和工厂。所有这些操作对于字段来说都是完全不可能的。

    【讨论】:

      【解决方案6】:

      第一个:

      public string MyString {get; set; }
      

      是一个属性;第二个(public string MyString)表示一个字段。

      不同之处在于,某些技术(用于实例的 ASP.NET 数据绑定)仅适用于属性,而不适用于字段。 XML 序列化也是如此:只有属性被序列化,字段不被序列化。

      【讨论】:

      • 错了。 XML 序列化确实会序列化公共字段。
      • 也许吧。但是当你从一个类创建一个对象数据源时,你只能使用属性,而不是字段。 (除非我做错了什么:P)
      • 干得好 ;) 但我再写一次,我喜欢 C# 语言中属性的强大作用。比 Java 更好地实现(因此从一开始) 许多,也许所有的 .net 解决方案都只适用于属性。 WPF、ASPX 等。
      【解决方案7】:

      在许多情况下,属性和字段可能看起来相似,但实际上并非如此。字段不存在属性的限制,反之亦然。

      正如其他人所提到的。您可以通过将访问器设为私有来将属性设为只读或只写。你不能用一个字段来做到这一点。属性也可以是虚拟的,而字段则不能。

      将属性视为 getXXX()/setXXX() 函数的语法糖。这就是它们在幕后实现的方式。

      【讨论】:

        【解决方案8】:

        字段和属性之间还有一个重要区别。

        使用 WPF 时,您只能绑定到公共属性。绑定到公共字段将不起作用。即使没有实现 INotifyPropertyChanged 也是如此(尽管您总是应该这样做)。

        【讨论】:

        • 干得好 ;) 但我再写一次,我喜欢 C# 语言中属性的强大作用。比 Java 更好地实现(因此从一开始) 许多,也许所有的 .net 解决方案都只适用于属性。 WPF、ASPX 等。
        【解决方案9】:

        在其他答案和示例中,我认为这个示例在某些情况下很有用。

        例如,假设您有一个 OnChange property,如下所示:

        public Action OnChange { get; set; }
        

        如果您想使用委托,则需要将其 OnChange 更改为 field,如下所示:

        public event Action OnChange = delegate {};
        

        在这种情况下,我们会保护我们的字段免受不必要的访问或修改。

        【讨论】:

          【解决方案10】:

          对于任何公共字段,您应该始终使用属性而不是字段。这样可以确保您的库 如果将来需要,可以在不破坏现有代码的情况下对任何字段进行封装。如果您将字段替换为现有库中的属性,那么所有使用您的库的依赖模块也需要重新构建。

          【讨论】:

          • “总是”是一个难以形容的词。在 C#(优于 Java)中,属性具有很强的地位,是(可能无一例外)在 ASP、WPF 等中“绑定”的主要/方法。但尽管如此,我可以想象没有存在属性的领域的设计(有时)
          猜你喜欢
          • 2017-11-08
          • 1970-01-01
          • 2019-03-07
          • 1970-01-01
          • 2010-09-29
          • 2019-07-17
          • 2012-04-24
          • 1970-01-01
          相关资源
          最近更新 更多