访问器不仅仅是字段。其他人已经指出了一些重要的差异,我将再添加一个。
属性参与接口类。例如:
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);
}
在这个代码示例中,消费者再次不知道如何填写属性。消费者只处理获取器和具体实现(以及背后的业务逻辑,例如测试名称是否为空),而专门的类 - 构建器和工厂则完成了其余操作。所有这些操作在字段中是完全不可能的。