在C#中,是什么使字段与属性不同?何时应该使用字段而不是属性?
当前回答
想想看:你有一个房间和一扇门可以进入这个房间。如果你想检查谁是如何进来并保护你的房间的,那么你应该使用财产,否则他们不会是任何门,每个人都很容易进来,没有任何规定
class Room {
public string sectionOne;
public string sectionTwo;
}
Room r = new Room();
r.sectionOne = "enter";
人们很容易进入第一区,没有任何检查
class Room
{
private string sectionOne;
private string sectionTwo;
public string SectionOne
{
get
{
return sectionOne;
}
set
{
sectionOne = Check(value);
}
}
}
Room r = new Room();
r.SectionOne = "enter";
现在你检查了这个人,知道他是否有什么不好的地方
其他回答
财产的主要优点是允许您更改对象上数据的访问方式,而不会破坏其公共接口。例如,如果您需要添加额外的验证,或者将存储的字段更改为计算字段,那么如果您最初将字段作为属性公开,则可以很容易地执行此操作。如果您只是直接公开了一个字段,那么您必须更改类的公共接口以添加新功能。这种改变会破坏现有的客户机,要求他们在使用新版本的代码之前重新编译。
如果您编写了一个为广泛使用而设计的类库(如数百万人使用的.NET Framework),这可能是一个问题。然而,如果您正在编写一个在小代码库内部使用的类(例如<=50K行),这真的不是什么大问题,因为没有人会受到您的更改的负面影响。在这种情况下,这真的只是个人喜好。
这里的第二个问题,“什么时候应该使用字段而不是属性?”,在另一个答案中只简短地提到了这个问题,也有点像这个问题,但没有太多细节。
总的来说,所有其他答案都是关于良好设计的一针见血的:比起暴露字段,更喜欢暴露财产。虽然你可能不会经常说“哇,想象一下,如果我把这里变成了一块地而不是一块地,情况会变得多么糟糕”,但想到你会说“哇!谢天谢地,我在这里用了一块土地而不是一处地”的情况,那就难得多了
但与财产相比,字段有一个优点,那就是它们可以用作“ref”/“out”参数。假设您有一个具有以下签名的方法:
public void TransformPoint(ref double x, ref double y);
假设您希望使用该方法来转换如下创建的数组:
System.Windows.Point[] points = new Point[1000000];
Initialize(points);
因为X和Y是财产,所以我认为这是最快的方法:
for (int i = 0; i < points.Length; i++)
{
double x = points[i].X;
double y = points[i].Y;
TransformPoint(ref x, ref y);
points[i].X = x;
points[i].Y = y;
}
这会很好的!除非你的测量结果证明了这一点,否则没有理由大惊小怪。但我认为,从技术上来说,它并不能保证像这样快:
internal struct MyPoint
{
internal double X;
internal double Y;
}
// ...
MyPoint[] points = new MyPoint[1000000];
Initialize(points);
// ...
for (int i = 0; i < points.Length; i++)
{
TransformPoint(ref points[i].X, ref points[i].Y);
}
我自己进行了一些测量,带字段的版本与带财产的版本(.NET 4.6、Windows 7、x64、发布模式、未附加调试程序)相比,大约需要61%的时间。TransformPoint方法的成本越高,差异就越不明显。若要自己重复此操作,请将第一行注释掉,并将其未注释掉。
即使上述方法没有性能优势,但在其他地方使用ref和out参数可能会有好处,例如在调用Interlocked或Volatile方法家族时。注意:如果这对您来说是新的,Volatile基本上是一种获得Volatile关键字提供的相同行为的方法。因此,就像volatile一样,它并没有像名字所暗示的那样神奇地解决所有线程安全问题。
我当然不想让人觉得我在提倡你说“哦,我应该开始公开字段,而不是财产。”关键是,如果你需要在调用中定期使用这些成员来接受“ref”或“out”参数,尤其是在可能是简单值类型的东西上,它不太可能需要财产的任何增值元素,那么可以进行一个参数。
一个重要的区别是接口可以有财产,但不能有字段。对我来说,这强调了财产应该用于定义类的公共接口,而字段则用于类的私有内部工作。通常,我很少创建公共字段,类似地,我也很少创建非公共财产。
在大多数情况下,它将是您访问的属性名,而不是变量名(字段)。原因是,在.NET中,尤其是在C#中,它被认为是保护类中的每一条数据的良好做法,无论是实例变量还是静态变量(类变量),因为它与类相关联。
使用相应的财产保护所有这些变量,这些属性允许您定义、设置和获取访问器,并在操作这些数据片段时执行验证等操作。
但在其他情况下,如Math类(System命名空间),类中内置了一些静态财产。其中一个是数学常数PI
例如数学PI
因为PI是一块定义良好的数据,我们不需要有多个PI副本,它总是相同的值。因此,静态变量有时用于在类的对象之间共享数据,但也通常用于只需要一个数据副本的常量信息。
财产是一种特殊的类成员。在财产中,我们使用预定义的Set或Get方法。它们使用访问器,通过访问器我们可以读取、写入或更改私有字段的值。
例如,让我们使用一个名为Employee的类,其中包含name、age和Employee_Id的私有字段。我们无法从类外部访问这些字段,但可以通过财产访问这些私有字段。
我们为什么使用财产?
公开类字段并公开它是有风险的,因为您无法控制分配和返回的内容。
为了通过示例清楚地理解这一点,让我们以一个具有ID、密码和姓名的学生班级为例。现在在这个例子中,公共领域的一些问题
ID不应为-ve。名称不能设置为空合格标记应为只读。如果缺少学生姓名,则不应返回姓名。
为了解决这个问题,我们使用Get和set方法。
// A simple example
public class student
{
public int ID;
public int passmark;
public string name;
}
public class Program
{
public static void Main(string[] args)
{
student s1 = new student();
s1.ID = -101; // here ID can't be -ve
s1.Name = null ; // here Name can't be null
}
}
现在我们以get和set方法为例
public class student
{
private int _ID;
private int _passmark;
private string_name ;
// for id property
public void SetID(int ID)
{
if(ID<=0)
{
throw new exception("student ID should be greater then 0");
}
this._ID = ID;
}
public int getID()
{
return_ID;
}
}
public class programme
{
public static void main()
{
student s1 = new student ();
s1.SetID(101);
}
// Like this we also can use for Name property
public void SetName(string Name)
{
if(string.IsNullOrEmpty(Name))
{
throw new exeception("name can not be null");
}
this._Name = Name;
}
public string GetName()
{
if( string.IsNullOrEmpty(This.Name))
{
return "No Name";
}
else
{
return this._name;
}
}
// Like this we also can use for Passmark property
public int Getpassmark()
{
return this._passmark;
}
}