前言

新年好,本篇开始进入第三章,《对象和类型》,深刻理解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. PDF分享:国外优秀数学教材选评

    <国外优秀数学教材选评>推荐书目下载 具体内容请查看原内容: http://www.library.fudan.edu.cn/wjzx/list/373-1-20.htm 或者http:/ ...

  2. cp/tar/用c语言编写程序 实现cp命令的效果

    1.cp (拷贝) 已存在文件路径  要拷贝的文件路径 实现cp命令的代码如下: #include <stdio.h> //因为要在命令中得到两个路径,所以要用到main函数的两个参数 i ...

  3. How Does Closure Work in Javascript?

    Simply, closure is the scope that it can visite and operate the variables outside of the function wh ...

  4. OpenCV3.30 画图函数

    画图函数(Draw Functions)都放在imgpro. 例如C++中用: #include <opencv2\imgproc.hpp>

  5. StringBuilder and StringBuffer

    StringBuilder sb = new StringBuilder(); /* 无参构造器 */ sb = new StringBuilder("abc"); /* 字符串构 ...

  6. bootstrap概述

    前面的话 Bootstrap是简单.灵活的用于搭建WEB页面的HTML.CSS.Javascript的工具集.Bootstrap基于HTML5和CSS3,具有漂亮的设计.友好的学习曲线.卓越的兼容性, ...

  7. 背水一战 Windows 10 (119) - 后台任务: 后台下载任务(任务分组,组完成后触发后台任务)

    [源码下载] 背水一战 Windows 10 (119) - 后台任务: 后台下载任务(任务分组,组完成后触发后台任务) 作者:webabcd 介绍背水一战 Windows 10 之 后台任务 后台下 ...

  8. 背水一战 Windows 10 (102) - 应用间通信: 剪切板

    [源码下载] 背水一战 Windows 10 (102) - 应用间通信: 剪切板 作者:webabcd 介绍背水一战 Windows 10 之 应用间通信 剪切板 - 基础, 复制/粘贴 text ...

  9. [转] 如何用kaldi训练好的模型做特定任务的在线识别

    转自:http://blog.csdn.net/inger_h/article/details/52789339 在已经训练好模型的情况下,需要针对一个新任务做在线识别应该怎么做呢? 一种情况是,用已 ...

  10. Java 线程池(ThreadPoolExecutor)原理解析

    在我们的开发中“池”的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 有关java线程技术文章还可以推荐阅读:<关于java多线程w ...