C#3.0+中属性和字段的区别
我意识到这似乎是C#中的字段和属性之间有什么区别的重复? 但是我的问题有一点点差别(从我的观点来看):
一旦我知道了
- 我不会使用我的课程“只能在属性上工作的技巧”
- 我不会在getter / setter中使用validation码。
有没有什么区别(风格/未来发展的除外),如设置属性的某种types的控制?
是否有任何其他区别:
public string MyString { get; set; }
和
public string myString;
(我知道,第一个版本需要C#3.0或更高版本,并且编译器确实创build了私有字段。)
封装。
在第二个例子中,你刚刚定义了一个variables,第一个variables周围有一个getter / setter。 所以,如果你决定在晚些时候想要validation这个variables,那将会容易得多。
另外他们在Intellisense中显示出不同的:)
编辑: OP更新更新的问题 – 如果你想忽略其他build议在这里,另一个原因是,它根本不是很好的面向对象devise。 如果你没有足够的理由去做, 总是select一个公共variables/字段的属性。
字段和属性看起来是一样的,但它们不是。 属性是方法,因此属性不支持某些东西,有些属性可能会发生,但从不属于字段。
以下是差异清单:
- 字段可以用作
out/ref
参数的input。 属性不能。 - 一个字段总是会在多次调用时产生相同的结果(如果我们省去了多个线程的问题)。 像
DateTime.Now
这样的属性并不总是等于它自己。 - 属性可能会抛出exception – 字段将永远不会这样做。
- 属性可能有副作用或需要很长时间才能执行。 田地没有任何副作用,并将始终如预期的给定types一样快。
- 属性支持getter / setter的不同可访问性 – 字段不(但是字段可以
readonly
) - 当使用reflection时,属性和字段被视为不同的
MemberTypes
因此它们的位置不同(例如GetFields
vsGetProperties
) - 与现场访问相比,JIT编译器可能会以不同的方式处理财产访问。 然而,它可能会编译到相同的本地代码,但差异的范围在那里。
一对快速,明显的差异
-
一个属性可以有访问者关键字。
public string MyString { get; private set; }
-
属性可以在后代覆盖。
public virtual string MyString { get; protected set; }
根本的区别在于,字段是存储指定types的数据的存储器中的位置。 属性表示执行一个或两个单元的代码来检索或设置指定types的值。 这些访问方法的使用在语法上是隐藏的,通过使用一个performance得像一个字段的成员(因为它可以出现在赋值操作的任一侧)。
访问者不仅仅是领域。 其他人已经指出了几个重要的差异,我将再添加一个。
属性参与接口类。 例如:
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
类无法进入无效状态,以及调用者从未分配的属性中取消null。
但我们可以进一步推动devise。 例如,界面可能不会处理setter。 说IPerson
接口的消费者IPerson
获得财产而不是设置它是完全合理的:
interface IPerson { string FirstName { get; } string LastName { get; } }
以前的Person
类的实现满足这个接口。 从消费者(消费IPerson
)的angular度来看,它允许调用者设置属性的事实是没有意义的。 例如,build设者会考虑具体实施的其他function:
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); }
在这个代码中,消费者并不了解财产制定者 – 这不是他的事。 消费者只需要获得者,并从界面获得获得者,即从合同中获得。
另一个完全有效的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); }
在这个代码示例中,消费者再次不知道填充属性。 消费者只处理getter和具体实现(以及后面的业务逻辑,比如testing名称是否为空),留给专门的类 – 构build者和工厂。 所有这些行动都是完全不可能的。
第一个:
public string MyString {get; set; }
是财产; 第二个( public string MyString
)表示一个字段。
不同之处在于,某些技术(实例的ASP.NET数据绑定)仅适用于属性,而不适用于字段。 XML序列化也是如此:只有属性被序列化,字段不被序列化。
属性和字段在许多情况下可能看起来相似,但它们不是。 对于字段不存在的属性有限制,反之亦然。
正如其他人所提到的。 您可以使属性为只读或只写,通过使访问者为私人。 你不能在一个领域做到这一点。 属性也可以是虚拟的,而字段不能。
将属性看作getXXX()/ setXXX()函数的语法糖。 这是他们如何在幕后实施的。
字段和属性之间还有一个重要的区别。
使用WPF时,只能绑定到公共属性。 绑定到公共领域是行不通的。 即使不执行INotifyPropertyChanged
(即使您始终应该),也是如此。
你应该总是使用属性,而不是字段的任何公共fields.This确保你的图书馆有能力实现任何领域的封装,如果将来需要,而不会破坏现有的代码。如果你replace现有库中的属性字段,那么所有的依赖你的库的模块也需要重build。