我意识到它似乎是C#中字段和属性之间有什么区别的重复?但我的问题略有不同(从我的观点来看):
一旦我知道了
我不会将我的课程用于"仅适用于属性的技术"和
我不会在getter/setter中使用验证代码.
是否有任何区别(风格/未来发展除外),如设置属性时的某种控制类型?
是否有任何额外的区别:
public string MyString { get; set; }
和
public string myString;
(我知道,第一个版本需要C#3.0或更高版本,并且编译器会创建私有字段.)
字段和属性看起来相同,但它们不是.属性是方法,因此有些东西不支持属性,有些东西可能会在属性中发生但从不在字段的情况下.
这是一个差异列表:
字段可用作out/ref
参数的输入.属性不能.
多次调用时,字段总是会产生相同的结果(如果我们忽略了多个线程的问题).诸如DateTime.Now
并不总是等于自身的财产.
属性可能会抛出异常 - 字段永远不会这样做.
属性可能有副作用或执行时间很长.字段没有副作用,并且总是与给定类型的预期一样快.
属性支持getter/setter的不同可访问性 - 字段不支持(但可以创建字段readonly
)
当使用反射的属性和字段将被视为不同的MemberTypes
,以便它们位于不同的(GetFields
VS GetProperties
例如)
与字段访问相比,JIT编译器可以非常不同地处理属性访问.然而,它可以编译为相同的本机代码,但差异的范围就在那里.
封装.
在第二个实例中,您刚刚定义了一个变量,在第一个实例中,变量周围有一个getter/setter.因此,如果您决定要在以后验证变量 - 那将更容易.
另外它们在Intellisense中的显示方式不同:)
编辑: OP更新更新问题 - 如果你想忽略其他建议,另一个原因是它不是好的OO设计.如果您没有充分理由这样做,请始终在公共变量/字段上选择一个属性.
几个快速,明显的差异
属性可以具有访问者关键字.
public string MyString { get; private set; }
在后代可以覆盖财产.
public virtual string MyString { get; protected set; }
根本区别在于字段是存储器中存储指定类型数据的位置.属性表示一个或两个代码单元,用于检索或设置指定类型的值.这些访问器方法的使用在语法上通过使用看起来像字段的成员来隐藏(因为它可以出现在赋值操作的任一侧).
访问者不仅仅是字段.其他人已经指出了几个重要的差异,我将再添加一个.
属性参与接口类.例如:
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.
但我们可以进一步推动设计.例如,接口可能不处理setter.说IPerson
接口的消费者只对获取属性感兴趣,而不是设置它是非常合理的:
interface IPerson { string FirstName { get; } string LastName { get; } }
Person
该类的先前实现满足此接口.从消费者(消费者IPerson
)的角度来看,它让调用者也设置属性这一事实毫无意义.例如,构建器将考虑具体实现的其他功能:
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和具体实现(以及它背后的业务逻辑,如测试名称是否为空)留给专门的类 - 构建器和工厂.所有这些操作完全不可能用于字段.
第一个:
public string MyString {get; set; }
是一个财产; 第二个(public string MyString
)表示一个字段.
不同之处在于,某些技术(实例的ASP.NET数据绑定)仅适用于属性,而不适用于字段.XML序列化也是如此:只有属性是序列化的,字段不是序列化的.