C#3.0+中属性和字段的区别

时间:2009-03-17 09:35:35

标签: c# properties c#-3.0 field automatic-properties

我意识到它似乎与What is the difference between a Field and a Property in C#?重复,但我的问题略有不同(从我的观点来看):

一旦我知道

  • 我不会将我的课程用于“仅适用于属性的技术”和
  • 我不会在getter / setter中使用验证码。

是否存在任何差异(样式/未来发展除外),如设置属性时的某种控制类型?

之间是否还有其他区别:

public string MyString { get; set; }

public string myString;

(我知道,第一个版本需要C#3.0或更高版本,并且编译器会创建私有字段。)

10 个答案:

答案 0 :(得分:151)

字段和属性看起来相同,但它们不是。属性是方法,因此有些属性不支持某些东西,有些东西可能会在属性中发生但从不在字段的情况下。

以下是差异列表:

  • 字段可用作out/ref参数的输入。属性不能。
  • 当多次调用时,字段将始终产生相同的结果(如果我们忽略多个线程的问题)。诸如DateTime.Now之类的属性并不总是等于它自己。
  • 属性可能会抛出异常 - 字段永远不会这样做。
  • 属性可能有副作用或需要很长时间才能执行。字段没有副作用,并且总是与给定类型的预期一样快。
  • 属性支持getter / setter的不同可访问性 - 字段不支持(但可以创建字段readonly
  • 使用反射时,属性和字段被视为不同的MemberTypes,因此它们的位置不同(例如GetFields vs GetProperties
  • 与现场访问相比,JIT编译器可以非常不同地处理属性访问。然而,它可以编译为相同的本机代码,但差异的范围就在那里。

答案 1 :(得分:115)

封装

在第二个实例中,您刚刚定义了一个变量,在第一个实例中,变量周围有一个getter / setter。因此,如果您决定在以后验证变量,那将会更容易。

另外,他们在Intellisense中的表现方式不同:)

编辑更新OP更新问题 - 如果您想忽略此处的其他建议,另一个原因是它不是优秀的OO设计。如果你没有充分的理由这样做,总是选择一个公共变量/字段的属性。

答案 2 :(得分:41)

一些快速,明显的差异

  1. 属性可以包含访问者关键字。

    public string MyString { get; private set; }
    
  2. 可以在后代中覆盖属性。

    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)

对于任何公共字段,您应该始终使用属性而不是字段。这可以确保您的库 如果将来需要,可以在不破坏现有代码的情况下为任何字段实现封装。如果用现有库中的属性替换字段,则还需要重建使用库的所有相关模块。