前言

新年好,本篇开始进入第三章,《对象和类型》,深刻理解C#的对象,对于使用好.Net类库非常重要。

01

类和结构

从使用角度看,结构和类的区别很小,比如,将结构定义转换为类,只需要将关键字struct改为class即可。创建结构的时候,也同样可以用关键字new。它们的本质区别是,结构是值类型,存储在栈上,而类存储在堆上。

但我还没碰到什么情况下需要使用结构。因为一般的业务代码,在设计时很难提炼出足够可泛化的含义,而如果只是少数情况下采用结构,对性能的提升微乎其微。但我们也可以发现在.Net框架下,还是比较多的使用了结构,这估计是因为:

  1. .Net作为千万程序员都在使用的框架和类库,性能上精益求精就有必要了
  2. 其次,我们发现.Net使用结构是成体系的,比如Point,Size, Rectangle等,大量的“形状”都采用了结构,这从对象化的设计角度看就非常有意义了

02

类的数据和函数称为类的成员。清晰的理解类的“数据成员”和“函数成员”,对于用好反射功能非常重要。因为反射针对不同的成员类型有不同的功能实现。

数据成员

数据成员是包含类的数据——字段、常量和事件的成员。特别注意的是,“事件”是数据成员。为什么呢?这后面会讲到,事件是一种特殊的委托,而委托实际上是类,所以事件的实例当然是数据。

函数成员

函数成员提供了操作类中数据的某些功能,包括方法、属性、构造函数和终结器(finalizer)、运算符以及索引器。方法、属性、构造函数大家经常碰到,会比较好理解。而对终结器、运算符、索引器来说,它们都是“特殊的方法”。

属性:函数组,即get函数和set函数的组合。

终结器:类似于构造函数,如果构造函数名为“FuncA”,终结器则名为“~FuncA”,类似Java的析构函数finalize()。

运算符:类的运算符可以重载,实现自定义功能,那么运算符归类为函数,也就好理解了。

索引器:将对象以集合的方式访问。

方法 / ref / out

方法的参数传递值得探讨。函数的参数传递有“值传递”和“引用传递”两种方式。在C#中,除非明确带关键字,所有的参数都通过值来传递。这让我们会产生一个疑问,因为在实际编程中,往往会使用引用类型作为参数,而又没有使用ref关键字时,此时传递的到底是值呢还是引用呢?答案是:传递的还是值。值传递时,仍然进行了值的复制,但复制的是“引用”,而不是对象本身。所以,我们也可以推论说:对于传递引用类型的参数,加不加ref,效果都同加ref是一样的。

和ref对应,还有个out关键字。使用out关键字定义的参数,out会强制要求函数中将变量初始化或赋值后再输出。实际上,使用或者不使用参数关键字,对应了实际使用方法中的三种常见场景:

  1. 不带关键字:传入参数,不传出
  2. ref关键字:传入参数,再传出
  3. out关键字:不接受参数传入,但传出参数。out和ref一样,传递的是引用。

命名参数

使用命名参数的作用是,书写调用时,参数的书写顺序不再受限制。比如:定义时为:

public void Test(int arg1, int arg2)

调用时可以为:

user.Test(arg2: 1, arg1: 2);

不过我没有用过命名参数,因为实在找不到应用它的场景。但我回想起在之前公司的基于Repository的架构下,倒是能为它找到用武之地。为什么呢?因为在Repository架构下,为遵循架构规则,需要为每一个表建立对外的业务访问接口,而复杂的业务需求又导致接口需要大量参数的方法重载,比如一个方法可能有超过10个参数定义。这时候,在调用方法的时候,万一有一两个参数写错了顺序,就会导致程序的bug。为了避免这种问题,使用命名参数,明确的指定 参数名:值,相当于程序员在写代码时就进行了明确的参数名检查,从而避免bug。但这种问题,是由于不合理的Repository架构导致的,当废弃了Repository架构后,这种应用场景不复存在。

可选参数

可选参数如:public void Test(int arg1, int arg2 = 10)

可选参数必须是方法定义的最后一个参数。

方法重载

书中说到“如果不能使用可选参数,就可以使用方法重载”。这说明了语言设计的倾向性。即原则上应该多使用可选参数,而避免过多的使用重载,毕竟方法重载的“代码复用度”一般不如可选参数。

何为“方法的重载”?方法的重载,即一个方法的几个版本有不同的签名(即,方法名相同,但参数的个数和/或类型不同,注:不包括返回值)。方法重载的参数限制:

  1. 两个方法不能仅在返回类型上有区别。
  2. 两个方法不能仅根据参数是声明为ref还是out来区分。

属性

属性(Property)的概念是:它是一个方法或一对方法,在客户端代码看来,它(们)是一个字段。这个定义非常有意思。所谓“客户端代码”,就是调用属性的代码。在调用者看来,属性和字段并无区别。区别在于内部,属性可以对其get/set访问器进行代码定制,从而实现更复杂的需求。

因为属性是特殊的方法,那么要实现一个功能时,是应该定义为属性还是定义为方法,这在12月25日的文章中有深入分析。

在有不少老的代码里,属性是这么定义的:

string column1;

public string Column1

{

get

{

return column1;

}

set

{

column1 = value;

}

}

这是因为在.Net2.0时,对属性的定义还没那么方便。虽然它已经比Java的get,set方法好多了,但还是比较繁琐。在更新版本的.Net下,已经可以使用这种方式定义属性:

public int Column1 { get; set; }

这叫做“自动实现的属性”。实际上它只是一个语法糖,本质并没有变。

get,set访问器可以带上修饰符。如果没有带上修饰符,那么它使用的是属性的修饰符。比如可以:

public int Column1 { get; private set; }

public int Column2 { get; }

public int Column3 { private get; set; }    //只写属性,不应该出现

内联

因为属性本质是方法,我们也看到实际使用中,大多数属性都是采用“自动实现属性”的,那么为了实现属性的偶尔才使用的“访问限制”功能,而将“字段访问”改为“方法”访问,会增加方法访问的额外开销吧?值得吗?

我们不用担心这个问题。因为.Net编译器已经将属性访问代码编译为“内联代码”而不是“函数调用”代码。

类是对象化编程的基本概念,内容非常多。下一篇将继续讲讲:构造函数、只读字段、匿名类型、结构详解、部分类、静态类、Object类、扩展方法,等。

觉得文章有意义的话,请动动手指,分享给朋友一起来共同学习进步。

欢迎关注本人微信公众号,更及时的关注最新文章(每周三篇原创文章,以及多篇专题文章):

附文:

c# 方法参数(传值,传引用,ref,out,params,可选参数,命名参数)

C#中的字段与属性的区别及属性的作用

上一篇:解读经典《C#高级编程》第七版 Page50-68.核心C#.Chapter2

解读经典《C#高级编程》第七版 Page68-79.对象和类型.Chapter3的更多相关文章

  1. c#高级编程第七版 学习笔记 第一章 .NET体系结构

    第一章      .NET体系结构 本章内容: 编译和运行面向.NET的代码 Microsoft中间语言(Microsoft Intermediate Language,MSIL或简称IL)的优点 值 ...

  2. c#高级编程第七版 学习笔记 第二章 核心c#

    第二章 核心C# 本章内容: 声明变量 变量的初始化和作用域 C#的预定义数据类型 在c#程序中使用条件语句.循环和跳转语句执行流 枚举 名称空间 Main()方法 基本的命令行c#编译器选项 使用S ...

  3. c#高级编程第七版 学习笔记 第三章 对象和类型

    第三章 对象和类型 本章的内容: 类和结构的区别 类成员 按值和按引用传送参数 方法重载 构造函数和静态构造函数 只读字段 部分类 静态类 Object类,其他类型都从该类派生而来 3.1 类和结构 ...

  4. 解读经典《C#高级编程》第七版 Page79-93.对象和类型.Chapter3

    前言 本篇我们继续讲解本章其余的部分:构造函数.只读字段.匿名类型.结构详解.部分类.静态类.Object类.扩展方法,等. 01 类 构造函数 构造函数是一种特殊的方法: 与类同名 没有返回值,甚至 ...

  5. ASP.NET MVC 4高级编程(第4版)

    <ASP.NET MVC 4高级编程(第4版)> 基本信息 作者: (美)Jon Galloway    Phil Haack    Brad Wilson    K. Scott All ...

  6. 《UNIX环境高级编程(第3版)》

    <UNIX环境高级编程(第3版)> 基本信息 原书名:Advanced Programming in the UNIX Environment (3rd Edition) (Addison ...

  7. 【转】apue《UNIX环境高级编程第三版》第一章答案详解

    原文网址:http://blog.csdn.net/hubbybob1/article/details/40859835 大家好,从这周开始学习apue<UNIX环境高级编程第三版>,在此 ...

  8. Linux - Unix环境高级编程(第三版) 代码编译

    Unix环境高级编程(第三版) 代码编译 本文地址:http://blog.csdn.net/caroline_wendy 时间:2014.10.2 1. 下载代码:http://www.apuebo ...

  9. Unix环境高级编程第三版中实例代码如何在自己的linux上运行的问题

    学习Linux已经有2个月了,最近被期末考试把进度耽误了,前几天把Unix环境高级编程看了两章,感觉对Linux的整体有了一些思路,今天尝试着对第一章涉及到的一个简单的交互式shell编译运行一下,结 ...

随机推荐

  1. 关闭iptables服务及命令行连接wifi及locale设置

    Ubuntu系统启动时都会自动启动iptables服务.如果想关闭该服务的自动启动,可以执行: sudo ufw disable 命令行方式连接某个SSID: sudo nmcli d wifi co ...

  2. python 递归实现汉诺塔算法

    def move(n,a,b,c): if (n == 1): print ( "第 ", n ," 步: 将盘子由 " ,a ," 移动到 &quo ...

  3. 与我们息息相关的internet服务(3)---电子邮件服务

    几年前了解了一下,现在再实施的时候,再了解,当然如果要到牛人张小龙28岁时的开发程度,可能还差一个筋斗云 在起步一个公司,从组建的技术上,可能要准备很多东西,其中一个就是我们熟悉的企业邮箱. 伊妹儿, ...

  4. 当input中的type值为file时,各浏览器的表现形式不同

    如果想使各浏览器下的表现形式相同,需要对该input元素隐藏,然后再改元素下方添加标签.其html写法如下 <div class="input-file"> <i ...

  5. 【python-appium】模拟手机按键搜索异常

    执行代码的过程中运行self.driver.press_keycode(84)设备没反映,则需要关闭#desired_caps["unicodeKeyboard"] = " ...

  6. 大数据之hiveSQL

    最近增加了学习java基础算法,包括几种排序算法,二叉树(前序,后序,中序),队列和栈,bmp搜索,广义搜索算法,迭代等等一些技巧(自己动手绝对比单纯的理论要强的多,多练练) HIVE是hadoop生 ...

  7. Visualizing the Git data model

    I wrote a small tool git-graph.py over the weekend which can be used to generate the object graph of ...

  8. 工作jQuery基础复习(一)

    1.prop() 方法 设置或者返回被选元素的属性和值 当该方法用于返回属性值时,则返回第一个匹配元素的值 当该方法设定属性值时,则为匹配元素集合设置一个或者多个属性/值对

  9. map put值 使用匿名函数

    List<Map<String, Object>> list = setRoleMenuRlMapper.selectMapByParams(params); // Map m ...

  10. PHPExcel防止大数以科学计数法显示

    在使用PHPExcel来进行数据导出时,常常需要防止有些数字(如手机号.身份证号)以科学计数法显示,我们可以采用下面的方式来解决: setCellValueExplicit第三个参数用PHPExcel ...