CLR类型设计之属性
在之前的随笔中,我们探讨了参数,字段,方法,我们在开始属性之前回顾一下,之前的探讨实际上串联起来就是OOP编程的思想,在接下来的文章中,我们还会讨论接口(就是行为),举个例子:我们如果要做一个学生档案,我们需要先抽象出来有那些对象实体,比如有一个学生类,里面有学生id,姓名,年龄,班级等字段。 但是这不能满足我们的需求,我们要做学生档案管理,需要知道学生的每科成绩,所以我们还需要一个成绩类,里面定义了学生的学生Id,科目,科目分数,下面是两个类的代码示例
public sealed class Student
{
//学员id
public int StudentId;
//姓名
public string Name;
//年龄
public int Age;
//班级名
public string classname;
}
public sealed class Score {
//学员id
public int StudentId;
//科目
public string SubjectName;
//成绩
public int Achievement;
}
有了这两个类以后,我们就可以创建一个获取学员成绩的方法,方法的代码示例就不写了,OOP的思想最重要的在于尽可能的模块化可复用,当然为了实现这些,还有继承,多态等去实现目的,但是在继续实现过程中你可能会发现一些问题,当我们需要使用上面类型的时候,可以通过实例化直接使用,如下:
Student stu = new Student();
stu.Name = "苏云";
stu.Age = ;
stu.classname = "fantasy";
但是如果我输入一个Age为-15,程序也可以通过,字段值就会被改变为-15,年龄是不存在负数的,所以这个值是不应该通过的,这就是今天的主题,属性设置,面向对象一条很重要的原则就是数据封装,意味类型字段永远不应该公开,否则很容易因为不恰当使用而破坏对象的状态,如上文我们输入的负值,当然还有一些其他原因,比如线程安全,字段为逻辑字段,其值存在于内存中的字节,通过某个算法获取得到。但是这样做就会导致一个问题,外部方法想要访问时,由于内部不公开,所以外部无法访问
CLR中提供了属性机制,我们完全可以不用担心上面的问题,我们改写一下例子一的代码示例,在实例化学生的时候,如果Age<1,就会抛出异常,可以看到是那个参数报出的异常,值是多少。
public sealed class Student
{
private int studentId;
private string name;
private int age;
private string classname;
//学员id
public int StudentId { get { return (studentId); }set { studentId = value; } }
//姓名
public string Name{ get { return (name); }set { name = value; } }
//年龄
public int Age
{
get { return (age); }
set
{
if (value<) throw new ArgumentOutOfRangeException("value", value.ToString(),"学生的年龄不能小于1岁");
age = value;
}
}
//班级名
public string Classname { get { return (classname); }set { classname = value; } }
}
属性机制使用起来很简单,每个属性都有名称和类型(类型不能为void),并且一个类中同一个字段名称只能出现一次,只需要get,set两个关键字,如果只有get那就是只读字段,只有set是只写字段。也可以在get上写计算方法获取到值,但是上述方法写起来是否觉得很麻烦?C#还支持自动属性实现,我们改写成绩类,示例代码如下,
public sealed class Score {
//学员id
public int StudentId { get; set; }
//科目
public string SubjectName { get; set; }
//成绩
public int Achievement { get; set; }
}
在C#中声明get;set但是却未提供对应方法,C#会自动声明一个私有字段,这样就可以很快定义一个属性,和写字段是一样的,但需要注意的是,属性不能作为out或ref参数传给方法,而字段可以
对象和初始化器
在之前的代码中,我们初始化学生类需要分两步,第一实例化,第二赋值,但实际上我们可以使用更简单的语法,对象初始化器初始化一个对象,只需要像下面这样一句话就可以初始化一个对象并且赋值,他做的事情和例子2是相同的。在集合中也可以使用初始化器初始化集合。
重点: 如果类没有无参的构造函数就会出现编译时错误
Student stu1 = new Student() {
Name="admain",Age=,Classname="fantasy"
};
我们提到集合也可以用初始化器的方法初始化,但是集合的初始化和对象并不一样,首先要求对象或字段继承了IEnummerable<T>接口,我们示例常见的Dictionary集合如何初始化
Dictionary<int, string> dic = new Dictionary<int, string> {
{ ,"张三"}, { ,"李四"}
};
//等价于
dic.Add(, "张三");
dic.Add(, "李四");
有参属性:索引器
一个属性的get访问器方法不接收参数,则称为无参属性,用起来就和访问字段一样,除了这些与字段相似的属性,还有一种有参属性,C#里称其为索引器,下文中所有有参属性都用索引器替代,C#使用数组风格的语法来公开索引器,看下面的示例:
class MyListBox
{
public ArrayList data = new ArrayList();
public object this[int idx] //this作索引器名称,idx是索引参数
{
get
{
if (idx > - && idx < data.Count)
{
return data[idx];
}
else
{
return null;
}
}
set
{
if (idx > - && idx < data.Count)
{
data[idx] = value;
}
else if (idx <= data.Count)
{
data.Add(value);
}
else
{
throw new ArgumentOutOfRangeException("idx", idx, "超出数组索引范围");
}
}
}
}
我们定义了一个类MyListBox,其中有一个ArrayList字段,在构造器中为其默认初始化了,在下面的代码中我们看到了如何声明一个索引器,我们返回的类型是object,索引器的返回类型一样不可以void,c#使用this[...]表达索引器,并且C#不支持静态索引器,尽管CLR支持静态有参属性,C#允许一个类型定义多个索引器,但是索引器参数集不能相同,其他一些语言中支持定义多个相同签名的索引器,因为其他一些语言中索引器可以自定命名,但是C#不允许这样做,因为C#中不允许开发人员指定索引器名称,C#为一个类型中的所有索引器都默认提供了一个叫做Item的名称,所以在C#中使用索引器只能通过不同签名来区分选择的索引器。
CLR并不区分有参属性和无参属性,对CLR来说,每个属性都只是类型中定义的一对方法,和一些元数据。下面的示例是如何调用索引器。使用起来也很简单吧
//初始化MyListBox
MyListBox ba = new MyListBox {
//集合初始化器初始化值
data = { "张三",,,},
};
//调用添加方法为其添加值
ba.data.Add("");
ba.data.Add();
for (int i = ; i < ba.data.Count; i++)
{
//使用索引器打印出指定值,具体实现请查看类中get方法
Console.WriteLine(ba.data[i]);
}
无参属性,初始化器,有参属性,有了这些你可以在你的方法中更好的使用字段,并且让你的数据封装更加安全,但是CLR作者本人却持有另外一种观点,作者觉得属性不如封装的方法。有兴趣的朋友可以自己翻阅CLR看看作者的观点。
CLR类型设计之属性的更多相关文章
- CLR类型设计之参数传递
写到这篇文章的时候,笔者回忆起来以前的开发过程中,并没有注意参数的传递是以值传递还是引用传递的,也是第一次了解到可变参数params,常用的不一定就代表理解,可能只是会用.接下来我们就一起回忆一下关于 ...
- CLR类型设计之泛型(二)
在上一篇文章中,介绍了什么是泛型,以及泛型和非泛型的区别,这篇文章主要讲一些泛型的高级用法,泛型方法,泛型接口和泛型委托,协变和逆变泛型类型参数和约束性,泛型的高级用法在平时的业务中用的不多,多用于封 ...
- CLR类型设计之方法与构造器
无论学习那门语言都要学习函数体,C#,JAVA,PHP,都会涉及到函数体,而C#的函数体成员并不少,方法和构造器就是函数体成员之一,函数体成员还包括但不限于:方法,属性,构造器,终结器,运算符及索引器 ...
- CLR类型设计之类型之常量和字段
前言 孔子说:温故而知新,可以为师矣.所以对于学习过的知识要多复习,并且每一次复习都要尽可能的去扩展,而不是书本上的几句理论知识.很多人都喜欢分享自己的学习内容,记录下生活的点点滴滴 ...
- CLR类型设计之泛型(一)
在讨论泛型之前,我们先讨论一下在没有泛型的世界里,如果我们想要创建一个独立于被包含类型的类和方法,我们需要定义objece类型,但是使用object就要面对装箱和拆箱的操作,装箱和拆箱会很损耗性能,我 ...
- 重温CLR(七 ) 属性和事件
无参属性 许多类型都定义了能被获取或更高的状态信息.这种状态信息一般作为类型的字段成员实现.例如一下类型包含两个字段: public sealed class Employee{ public str ...
- 指定的架构无效。错误: CLR 类型到 EDM 类型的映射不明确
在使用WebService开发时,同时使用了EF和linq,查询数据时,使用linq(查询订单)可以正常拉出数据, 但是使用EF(查询用户)却会报以下错误: {"指定的架构无效.错误: \r ...
- 异常跟踪之CLR 类型到 EDM 类型的映射不明确
异常信息: "指定的架构无效.错误: CLR 类型到 EDM 类型的映射不明确,因为多个 CLR 类型与 EDM 类型“Person”匹配. 以前找到的是 CLR 类型“A.Person”, ...
- [CLR via C#]10. 属性
一.无参属性 对于字段,强烈建议将所有的字段都设为private.如果允许用户或类型获取或设置状态信息,就公开一个针对该用途的方法.封装了字段访问的方法通常称为访问器(accessor)方法.访问器方 ...
随机推荐
- JavaScript链式调用
1.什么是链式调用? 这个很容易理解,例如 $('text').setStyle('color', 'red').show(); 一般的函数调用和链式调用的区别:链式调用完方法后,return thi ...
- Java面向对象 集合(上)
Java面向对象 集合(上) 知识概要: (1)体系概述 (2)共性方法 (3)迭代器 (4)list集合 (5)Set 集合 体系概述: 集 ...
- Javascript 面向对象编程—封装
前 言 Javascript是一种基于对象(object-based)的语言,你遇到的所有东西几乎都是对象.但是,它又不是一种真正的面向对象编程(OOP)语言,因为它的语法中没有class(类) ...
- Spring读书笔记——bean解析
前情回顾 上篇<Spring读书笔记--bean加载>我们从代码角度介绍了有哪些类负责解析XML文件,又是如何一步步从XML格式脱变成我们熟悉的bean的,直到DefaultBeanDef ...
- 组件 layui 常用控件下拉框的应用
下拉框的显示样式: 针对下拉框的绑定等操作时,在最后务必调用一次 form.render(); 1.基本定义: <div class="layui-form-item"> ...
- NopCommerce 3. Controller 分析
1. 继承关系,3个abstract类 System.Web.Mvc.Controller Nop.Web.Framework.Controllers.BaseController Nop.Admin ...
- ubuntu tftp-server 服务器安装与配置
第一步:安装tftp服务sudo apt-get install tftpd tftp openbsd-inetd第二步:目录配置vi /etc/inetd.conf修改文件夹为根目录下的tftpbo ...
- Android Stuido 提高开发效率的插件
好久没有更新博客了,最近搞个listview搞得半死不活的,心累~~ 今天带来的是Android Studio插件的整理,全是我已经安装使用的,写这篇博文的目的也是因为我怕我自己给忘记怎么用(尴尬) ...
- js金钱转大写
function Arabia_to_Chinese(Num) { // var money=$("#deal_amount").val(); for ( i = Num.leng ...
- 线性表(存储结构数组)--Java 实现
/*线性表的数组实现 *特点:插入删除慢需要平均移动一半的数据,查找较快 *注意:有重复和无重复的数据对应的操作会有些不同 *注意数组一旦创建其大小就固定了 *Java集合长度可变是由于创建新的数组将 ...