我意识到它似乎与What is the difference between a Field and a Property in C#?重复,但我的问题略有不同(从我的观点来看):
一旦我知道
是否存在任何差异(样式/未来发展除外),如设置属性时的某种控制类型?
之间是否还有其他区别:
public string MyString { get; set; }
和
public string myString;
(我知道,第一个版本需要C#3.0或更高版本,并且编译器会创建私有字段。)
答案 0 :(得分:151)
字段和属性看起来相同,但它们不是。属性是方法,因此有些属性不支持某些东西,有些东西可能会在属性中发生但从不在字段的情况下。
以下是差异列表:
out/ref
参数的输入。属性不能。 DateTime.Now
之类的属性并不总是等于它自己。readonly
)MemberTypes
,因此它们的位置不同(例如GetFields
vs GetProperties
)答案 1 :(得分:115)
封装
在第二个实例中,您刚刚定义了一个变量,在第一个实例中,变量周围有一个getter / setter。因此,如果您决定在以后验证变量,那将会更容易。
另外,他们在Intellisense中的表现方式不同:)
编辑更新OP更新问题 - 如果您想忽略此处的其他建议,另一个原因是它不是优秀的OO设计。如果你没有充分的理由这样做,总是选择一个公共变量/字段的属性。
答案 2 :(得分:41)
一些快速,明显的差异
属性可以包含访问者关键字。
public string MyString { get; private set; }
可以在后代中覆盖属性。
public virtual string MyString { get; protected set; }
答案 3 :(得分:14)
根本区别在于字段是存储器中存储指定类型数据的位置。属性表示一个或两个代码单元,用于检索或设置指定类型的值。这些访问器方法的使用在语法上通过使用看起来像字段的成员来隐藏(因为它可以出现在赋值操作的任一侧)。
答案 4 :(得分:10)
访问者不仅仅是字段。其他人已经指出了几个重要的差异,我将再添加一个。
属性参与接口类。例如:
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
)的角度来看,它让调用者也设置属性这一事实毫无意义。具体实现的附加功能由例如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);
}
在这段代码中,消费者不了解财产制定者 - 了解它并不是他的事。消费者只需要吸气剂,他从接口获得吸气剂,即合同。
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和具体实现(以及它背后的业务逻辑,如测试名称是否为空)留给专门的类 - 构建器和工厂。使用字段完全不可能完成所有这些操作。
答案 5 :(得分:7)
第一个:
public string MyString {get; set; }
是一个财产;第二个(public string MyString
)表示一个字段。
不同之处在于,某些技术(实例的ASP.NET数据绑定)仅适用于属性,而不适用于字段。 XML序列化也是如此:只有属性被序列化,字段不被序列化。
答案 6 :(得分:3)
在许多情况下,属性和字段似乎相似,但它们不是。字段不存在的属性存在限制,反之亦然。
正如其他人所说。您可以通过将属性设置为私有来使属性成为只读或只写。你不能用字段做到这一点。属性也可以是虚拟的,而字段则不能。
将属性视为getXXX()/ setXXX()函数的语法糖。这是他们在幕后实施的方式。
答案 7 :(得分:1)
字段和属性之间还有另一个重要区别。
使用WPF时,您只能绑定到公共属性。绑定到公共字段将不工作。即使不实施INotifyPropertyChanged
(即使你总是应该),也是如此。
答案 8 :(得分:1)
在其他答案和示例中,我认为这个例子在某些情况下很有用。
例如,假设您有以下 OnChange
property
:
public Action OnChange { get; set; }
如果您想使用代理人而不是需要将代理商 OnChange
更改为field
,请执行以下操作:
public event Action OnChange = delegate {};
在这种情况下,我们会保护我们的字段免受不必要的访问或修改。
答案 9 :(得分:0)
对于任何公共字段,您应该始终使用属性而不是字段。这可以确保您的库 如果将来需要,可以在不破坏现有代码的情况下为任何字段实现封装。如果用现有库中的属性替换字段,则还需要重建使用库的所有相关模块。