142

我意识到它似乎与C# 中的字段和属性有什么区别?但我的问题略有不同(从我的角度来看):

一旦我知道

  • 我不会将我的课程与“仅适用于属性的技术”一起使用,并且
  • 我不会在 getter/setter 中使用验证代码。

是否有任何区别(样式/未来开发的除外),例如设置属性时的某种类型的控件?

是否有任何额外的区别:

public string MyString { get; set; }

public string myString;

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

4

10 回答 10

165

字段和属性看起来相同,但实际上并非如此。属性是方法,因此属性不支持某些事情,并且某些事情可能会发生在属性上,但绝不会发生在字段的情况下。

以下是差异列表:

  • 字段可以用作out/ref参数的输入。属性不能。
  • 一个字段在多次调用时总是会产生相同的结果(如果我们忽略了多个线程的问题)。诸如此类的属性DateTime.Now并不总是等于其自身。
  • 属性可能会抛出异常——字段永远不会那样做。
  • 属性可能有副作用或需要很长时间才能执行。字段没有副作用,并且对于给定类型总是尽可能快。
  • 属性支持 getter/setter 的不同可访问性 - 字段不支持(但可以制作字段readonly
  • 使用反射时,属性和字段被视为不同MemberTypes,因此它们的位置不同(例如GetFieldsvs )GetProperties
  • 与字段访问相比,JIT 编译器可能会以非常不同的方式处理属性访问。然而,它可以编译成相同的本机代码,但存在差异的范围。
于 2009-03-17T11:15:57.007 回答
119

封装。

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

此外,它们在 Intellisense 中的显示方式也不同 :)

编辑:更新 OPs 更新问题 - 如果您想忽略此处的其他建议,另一个原因是它根本不是好的 OO 设计。如果您没有很好的理由这样做,请始终选择公共变量/字段上的属性。

于 2009-03-17T09:38:05.120 回答
43

几个快速,明显的区别

  1. 属性可以具有访问器关键字。

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

    public virtual string MyString { get; protected set; }
    
于 2009-03-17T09:39:37.580 回答
14

根本区别在于,字段是内存中存储指定类型数据的位置。一个属性表示一个或两个代码单元,它们被执行以检索或设置指定类型的值。通过使用看起来像字段的成员(因为它可以出现在赋值操作的任一侧),这些访问器方法的使用在语法上是隐藏的。

于 2009-03-17T09:46:46.580 回答
11

访问器不仅仅是字段。其他人已经指出了几个重要的区别,我将再添加一个。

属性参与接口类。例如:

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类不进入无效状态,又保护调用者不从未分配的属性中获取空值。

但我们可以进一步推动设计。例如,接口可能不处理 setter。可以说IPersoninterface 的使用者只对获取属性感兴趣,而不是对设置它感兴趣:

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);
}

在这段代码中,消费者不知道属性设置器——知道它不是他的事。消费者只需要getter,他从接口,即从合约中获取getter。

另一个完全有效的实现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);
}

在此代码示例中,消费者再次不知道填充属性。Consumer 只处理 getter 和具体实现(以及它背后的业务逻辑,比如测试 name 是否为空)留给专门的类 - 构建器和工厂。所有这些操作对于字段来说是完全不可能的。

于 2013-12-24T12:12:03.050 回答
8

第一个:

public string MyString {get; set; }

是财产;第二个 ( public string MyString) 表示一个字段。

不同之处在于,某些技术(用于实例的 ASP.NET 数据绑定)仅适用于属性,而不适用于字段。XML 序列化也是如此:只有属性被序列化,字段不被序列化。

于 2009-03-17T09:38:41.810 回答
2

在许多情况下,属性和字段可能看起来相似,但事实并非如此。对于字段不存在的属性存在限制,反之亦然。

正如其他人所提到的。您可以通过将访问器设为私有来将属性设为只读或只写。你不能用一个字段来做到这一点。属性也可以是虚拟的,而字段则不能。

将属性视为 getXXX()/setXXX() 函数的语法糖。这就是它们在幕后实现的方式。

于 2009-03-17T10:09:53.140 回答
1

字段和属性之间还有另一个重要区别。

使用 WPF 时,只能绑定到公共属性。绑定到公共字段将不起作用。即使没有实施也是如此INotifyPropertyChanged(即使你总是应该这样做)。

于 2014-05-07T18:38:29.447 回答
1

在其他答案和示例中,我认为这个示例在某些情况下很有用。

例如,假设您有以下类似内容:OnChange property

public Action OnChange { get; set; }

如果您想使用委托,则需要将其更改OnChangefield

public event Action OnChange = delegate {};

在这种情况下,我们会保护我们的领域免受不必要的访问或修改。

于 2018-01-27T23:20:26.740 回答
0

对于任何公共字段,您应该始终使用属性而不是字段。这可确保您的库能够在将来需要时对任何字段实施封装,而不会破坏现有代码。如果您将字段替换为现有库中的属性,那么所有使用您的库的依赖模块也需要重建。

于 2014-08-09T15:32:33.153 回答