Difference between Property and Field in C# 3.0+
我意识到它似乎是C中字段和属性之间的区别的复制品。但我的问题有一点不同(从我的观点来看):
一旦我知道了
- 我不会将我的课程与"只在属性上工作的技术"一起使用,并且
- 我不会在getter/setter中使用验证代码。
在设置属性时是否存在任何差异(除了样式/未来开发的差异),比如某种类型的控件?
以下两者之间是否有其他区别:
1 | public string MyString { get; set; } |
和
1 | public string myString; |
(我知道,第一个版本需要C 3.0或更高版本,编译器确实创建了私有字段。)
字段和属性看起来是相同的,但它们不是。属性是方法,因此,某些属性不支持某些内容,某些内容可能与属性一起发生,但在字段的情况下永远不会发生。
以下是差异列表:
- 字段可以用作
out/ref 参数的输入。属性不能。 - 一个字段在多次调用时总是产生相同的结果(如果我们不考虑多个线程的问题)。像
DateTime.Now 这样的属性并不总是等于它本身。 - 属性可能引发异常-字段永远不会这样做。
- 属性可能有副作用,或者需要很长时间才能执行。字段没有副作用,并且总是与给定类型的预期速度一样快。
- 属性支持getter/setter的不同可访问性-字段不支持(但可以将字段设置为
readonly )。 - 使用反射时,属性和字段被视为不同的
MemberTypes ,因此它们的位置不同(例如,GetFields 与GetProperties )。 - 与字段访问相比,JIT编译器对属性访问的处理可能非常不同。但是,它可以编译成相同的本机代码,但不同之处在于。
封装。
在第二个实例中,您刚刚定义了一个变量,在第一个实例中,变量周围有一个getter/setter。因此,如果您决定以后验证变量,这会容易得多。
另外,它们在智能意义上的表现也不同:)
编辑:更新ops更新的问题-如果你想忽略这里的其他建议,另一个原因是它根本不是一个好的oo设计。如果你没有一个很好的理由去做,总是选择一个属性而不是一个公共变量/字段。
几个快速明显的区别
属性可以具有访问器关键字。
1 | public string MyString { get; private set; } |
属性可以在子代中被重写。
1 | public virtual string MyString { get; protected set; } |
基本的区别在于,字段是内存中存储指定类型数据的位置。属性表示为检索或设置指定类型的值而执行的一个或两个代码单元。这些访问器方法的使用在语法上是通过使用一个表现类似于字段的成员来隐藏的(因为它可以出现在赋值操作的任一侧)。
访问器不只是字段。其他人已经指出了几个重要的区别,我还要再加一个。
属性参与接口类。例如:
1 2 3 4 5 | interface IPerson { string FirstName { get; set; } string LastName { get; set; } } |
这个接口可以通过几种方式得到满足。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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; } } ... } |
在这个实现中,我们既保护
但我们可以进一步推进设计。例如,接口可能不处理setter。可以说,
1 2 3 4 5 | interface IPerson { string FirstName { get; } string LastName { get; } } |
以前的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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和具体的实现(以及它背后的业务逻辑,比如测试名称是否为空)留给专门的类——构建者和工厂。所有这些行动在战场上都是完全不可能的。
第一个:
1 | public string MyString {get; set; } |
是一个属性;第二个属性(
区别在于,某些技术(实例的ASP.NET数据绑定)只对属性有效,而对字段无效。XML序列化也是如此:只序列化属性,不序列化字段。
在许多情况下,属性和字段看起来很相似,但事实并非如此。对字段不存在的属性有限制,反之亦然。
正如其他人提到的。您可以通过将属性的访问器设为私有来将其设为只读或只写。你不能用字段来实现。属性也可以是虚拟的,而字段不能。
把属性看作getxxx()/setxxx()函数的语法糖。这就是如何在幕后实现它们。
在其他答案和例子中,我认为这个例子在某些情况下是有用的。
例如,假设您有一个
1 | public Action OnChange { get; set; } |
如果您想使用代理,而不需要将其从
1 | public event Action OnChange = delegate {}; |
在这种情况下,我们保护我们的领域不受不必要的访问或修改。
字段和属性之间还有一个重要的区别。
使用WPF时,只能绑定到公共属性。绑定到公共字段将不起作用。即使不实现
对于任何公共字段,都应使用属性而不是字段。这将确保您的库如果将来需要,可以对任何字段实现封装,而不破坏现有代码。如果用现有库中的属性替换字段,则使用库的所有依赖模块也需要重新生成。