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

我意识到这看起来像是 C # 中字段和属性的区别是什么?的复制品,但是我的问题有一点不同(从我的角度来看) :

一旦我知道了

  • 我不会将我的类与“仅在属性上有效的技术”和
  • 我不会在 getter/setter 中使用验证代码。

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

下列两者之间是否还有其他区别:

public string MyString { get; set; }

还有

public string myString;

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

77592 次浏览

封装。

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

另外,它们在智能感知中的显示方式也不同:)

编辑: 更新操作系统更新问题-如果你想忽略这里的其他建议,另一个原因是它根本不是好的面向对象设计。如果你没有很好的理由这样做,一直都是会选择一个属性而不是一个公共变量/字段。

第一个:

public string MyString {get; set; }

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

区别在于,某些技术(实例的 ASP.NET 数据绑定)只能在属性上工作,而不能在字段上工作。 XML 序列化也是如此: 只序列化属性,不序列化字段。

有几处明显的不同之处

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

    public string MyString { get; private set; }
    
  2. A property can be overridden in descendents.

    public virtual string MyString { get; protected set; }
    

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

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

正如其他人提到的。可以将属性的访问器设置为私有,从而使其成为只读或只写属性。你不能在田里做这个。属性也可以是虚拟的,而字段不能。

可以将属性看作 getXXX ()/setXXX ()函数的语法糖。

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

下面是一些不同之处:

  • 字段可用作 out/ref参数的输入。属性不能。
  • 一个字段在多次调用时总是产生相同的结果(如果我们省略了多个线程的问题)。像 DateTime.Now这样的属性并不总是等于它自己。
  • 属性可能会抛出异常-字段永远不会这样做。
  • 属性可能有副作用,或者需要很长时间才能执行。字段没有副作用,并且总是以给定类型所期望的速度运行。
  • 属性对 getter/setter 支持不同的可访问性——字段不支持(但字段可以设为 readonly)
  • 当使用反射时,属性和字段被视为不同的 MemberTypes,因此它们的位置也不同(例如,GetFieldsGetProperties)
  • 与字段访问相比,JIT 编译器对属性访问的处理方式可能非常不同。然而,它可以编译成相同的本机代码,但是存在差异。

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

属性参与接口类。例如:

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。

但我们可以进一步推进设计。例如,interface 可能不处理 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);
}

在这个代码中,消费者不知道财产设置者-这不是他的业务知道它。消费者只需要 getter,他从接口中获得 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);
}

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

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

使用 WPF 时,只能绑定到公共属性。绑定到公共字段将使 没有工作。即使在没有实现 INotifyPropertyChanged的情况下也是如此(即使您总是应该这样做)。

对于任何公共字段,应始终使用属性而不是字段 有能力实现封装任何字段,如果需要在未来不破坏现有的代码。如果您替换字段的属性在现有的库,那么所有的依赖模块使用您的库也需要重建。

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

例如,假设你有一个 OnChange property,如下所示:

public Action OnChange { get; set; }

如果你想使用委托,你需要像这样把它从 OnChange改为 field:

public event Action OnChange = delegate {};

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