属性详解
属性使用起来像字段,本质上是方法,属性是类成员重要的组成部分,访问属性和访问公共数据成员一样方便,但是属性的访问机制和公共数据成员的访问机制完全不同,它实际上是调用了内部相应的方法,那我们为什么不直接使用公共数据成员,而要使用属性了,首先我想说一下属性和字段的区别,C#是面向组件的编程,而字段是一个组件的实现细节,实现细节应该隐藏起来,,是不能暴露在组件外部的,应该定义为私有的,而属性是组件提供给外部的访问接口,必须公开。
首先我想定义一个学生类,为了紧扣主题,该学生类暂时只包含学生的基本信息:年龄、姓名、学生人数(静态只读)。不用充分考虑,我们肯定会写出这样的一段代码:
class Student
{
public int _age;
public string _name;
public static readonly _count;
}
下面我们来分析一下它的缺点:在这个类中,我们定义了三个公有数据成员,外部在访问这三个数据成员时可以随便的修改它们的值,严重违背了C#的封装机制,所以这样定义并不合理,所以我们需要使用属性来实现对数据的封装。为了防止外部能访问到这三个数据成员,将其私有化。
class Student
{
private int _age; //私有字段
private string _name; //私有字段
private static int _count; //私有字段
public int Age //属性Age
{
get { return _age; }
set
{
if (value < 0 || value > 100)
{
throw new ArgumentOutOfRangeException("the value must be in 0 between 100");
}
else
{
_age = value;
}
}
}
public string Name //属性Name
{
get { return _name; }
set { _name = value; }
}
public static int Count //只读静态属性
{
get { return _count; }
}
}
几点说明:
- 属性要定义名称和类型,且类型不能是void。
- 属性是不能重载的。我们不能定义名称相同、类型不同的两个属性。
- 属性一般需要定义get和set方法来操作类内部的私有支持字段,如上面的_name, _age等。Set方法中包含隐藏参数叫做value,表示赋给属性的值。
- 只读只写属性:可以通过省略set来定义只读属性(如Count属性),或者省略get来定义只写属性。
- CLR支持静态、实例、抽象和虚属性。例子中的Name和Age就是我们最常用的实例属性,Count就是静态只读属性的例子
- 使用属性时会产生相应的智能感知,就像使用公用字段一样
自动实现的属性——AIP
public int Age{set;get;}
注意一下几点:
① AIP的get和set方法中不能添加断点调试。
② AIP属性必须是同时可读可写的。如果只定义get或者只定义set,则必须两个都显式实现,不能使用AIP。
③ 想要序列化或者反序列化的类中,不要定义AIP。因为运行时序列化引擎将字段名持久化到了序列化流中,而且每次编译时这个名字还有可能改变。
索引器:通过给访问器添加参数,一个索引器可以访问更多的属性
class IndexerExample<T>
{
private T[] array = new T[2];
public T this[int i]
{
get { return array[i]; }
set { array[i] = value; }
}
}
class Application
{
static void Main()
{
IndexerExample<string> indexer = new IndexerExample<string>();
indexer[0] = "guowenhui";
indexer[1] = "dongyaguang";
}
}
这里我想再谈一个问题,可能与本篇的主题不太相关,就当是本篇的延伸,
大多数的ASP.NET服务器控件存储其属性值的方式都是通过ViewState来存储的,而不是通过类的私有化字段进行存储的,这里我想解释一下为什么。
首先我们需要知道ViewState是什么,ViewState是一个定义在System.Web.UI.Control类中的一个Protected型的属性,同时所有的服务器控件及用户自定义控件都是继承自System.Web.UI.Control,因此这些控件均具有ViewState属性。ViewState的项是可以存储任何类型的值的(实际上任何赋值给ViewState的变量都会被装箱为Object类型的变量),很多人可能会这么想,当我们给一个TextBox控件的Text属性赋值的时候,应该是这样进行存储的
Public String Text
{
get { return _text; }
set { _text=value; }
}
这里_text为类中定义的私有字段成员,但是我们注意,大部分的ASP.NET服务器控件在存储属性值的时候并不是通过这种方式实现的,而是通过ViewState,通过Reflector查看TextBox.Text属性的源代码你可以看到类似如下的代码:
Public String Text
{
get { return (String)ViewState["Text"]; }
set { ViewState["Text"]=value; }
}
所以在设计自定义组件的时候,最好遵循这种方式,这里还有一个问题,在以ViewState位存储方式的时候,如果实现属性的默认值,我们因该怎么做了,很多人可能会觉得是这样实现的
class TextControl
{
private String _text = "DefaultValue";
public String Text
{
get { return _text; }
set { _text = value; }
}
}
这里我想用控件的ViewState属性来设定默认值,我们应该怎么做了。
class TextControl
{
public String Text
{
get { return ViewState["Text"]==null?"DefaultValue":(String)ViewState["Text"]; }
set { ViewState["Text"] = value; }
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架