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)方法.访问器方 ...
随机推荐
- 搭建git远程服务器三步骤
以前都是使用git,这次由于工作需要,需要自己搭建一个远程git服务器.根据网上的 介绍,捣鼓了一下午,终于把远程git服务器搞定了,这里,做个总结. 搭建git远程服务,首先要安装git和ssh,以 ...
- win10 UWP 应用设置
win10 UWP 应用设置 简单的把设置需要的,放到微软自带的LocalSettings LocalSettings.Values可以存放几乎所有数据 如果需要存放复合数据,一个设置项是由多个值组成 ...
- visio移动形状 上下左右键 移动滚动条
今天在用visio写作业,想微移visio的形状,于是按上下左右方向键,结果移动的是滚动条. 那么如何让visio按上下左右移动的是形状,其实按Scroll键就好了.
- Linux 可运行进程 Runnable Process Definition
From : http://www.linfo.org/runnable_process.html 一个可运行的进程是指该进程的进程状态为TASK_RUNNING. 进程,也可被称为任务,是指一个程序 ...
- ELK简介
什么是ELK ELK是ElasticSearch,LogStash以及Kibana三个产品的首字母缩写.是可以和商业产品 Splunk 相媲美开源项目. 2013 年,Logstash 被 Elast ...
- [译]ASP.NET Core 2.0 全局配置项
问题 如何在 ASP.NET Core 2.0 应用程序中读取全局配置项? 答案 首先新建一个空项目,并添加两个配置文件: 1. appsettings.json { "Section1&q ...
- LINUX 笔记-文本过滤
^ 只匹配行首 $ 只匹配行尾 * 一个单字符后紧跟*,匹配0个 ...
- Java基础总结--IO总结2
1.键盘录入--Java具有特定的对象封装这些输入输出设备在System类定义 in-InputStream类型和out-PrintStream类型成员变量阻塞是方法:read()无数据就阻塞wind ...
- PhiloGL学习(5)——神说要有光,便有了光
前言 上一篇文章中介绍了如何创建三维对象及加载皮肤,本文为大家介绍如何为场景添加光源. 一. 原理分析 光在任何地方都是非常重要的,无论在哪里都说是要发光发热,光和热也是分不开的.光线分为点光源和线光 ...
- VC++6.0在win8.1系统下运行失败的解决办法
在win8.1系统下安装了VC++6,.0编译软件之后,发现打不开.出现下面的错误: 解决办法: 安装文件目录:Microsoft Visual Studio--common--MSDev98--Bi ...