【读书笔记】C#高级编程 第三章 对象和类型
(一)类和结构
类和结构实际上都是创建对象的模板,每个对象都包含数据,并提供了处理和访问数据的方法。
类和结构的区别:内存中的存储方式、访问方式(类是存储在堆上的引用类型,结构是存储在栈的值类型)和它们的一些特征(如结构不支持继承)。
较小的数据建议使用结构来提高性能。
创建类使用 class 关键字
例子:
class ClassTest { }
创建结构使用 struct 关键字
例子:
struct StructTest { }
对于类和结构,都是用关键字new来声明实例:这个关键字创建对象并对其初始化
ClassTest classTest = new ClassTest();
StructTest structTest = new StructTest();
(二)类
类中的数据和函数成为类的成员。
1、数据成员
数据成员是包含类的数据——字段、常量和事件的成员。
例子:
以下代码创建了一个名为People的类,其中包含了一个常量Country和一个字段Name
class People {
public const string Country = "中国";
public string Name;
}
例子:
现在我们实例化People对象,可以使用Object.FieldName来字段,使用ClassName.ConstName来访问常量(因为常量总是静态)
static void Main(string[] args)
{
People people = new People();
people.Name = "张三";
Console.WriteLine("{0}的国家是{1}", people.Name, People.Country);
Console.ReadKey();
}
运行以上代码,结果如下:

2、函数成员
函数成员提供了操作类中数据的某些功能,包括方法、属性、构造函数和终结器、运算符以及索引器。
- 方法是与某个类相关的函数,与数据成员一样,函数成员默认为实例成员,使用static修饰符可以把方法定义为静态方法。
- 属性是可以从客户端访问的函数组,其访问方式与访问类的公共字段类似。
- 构造函数是在实例化对象时自动调用的特殊函数。它们必须与所属的类同名,且不能有返回类型。构造函数用于初始化字段的值。
- 终结器类似于构造函数,但是在CLR检测到不再需要某个对象时调用它。它们的与所属类同名,但前有一个“~”符号。无法预测什么时候调用终结器。
- C#允许指定已有的运算符应用于自己的类(运算符重载)。
- 索引器允许对象以数组或集合的方式进行索引。
(1)方法
正式的C#术语区分函数和方法。
1.方法的声明
在C#中,方法的定义包括任意方法修饰符(如方法的可访问性)、返回值的类型,然后依次是方法名、输入参数列表(用圆括号括起来)和方法体(用花括号括起来)。
例子:
public bool IsLeft(string direction)
{
if (direction=="left")
{
return true;
}
else
{
return false;
}
}
以上代码中,public是方法的可访问性,bool是返回值的类型,IsLeft是方法名,(string direction)是输入参数,{}内的是方法体
2.方法的调用
static void Main(string[] args)
{
Program program = new Program();
bool isLeft = program.IsLeft("left");
Console.ReadKey();
}
public bool IsLeft(string direction)
{
if (direction == "left")
{
return true;
}
else
{
return false;
}
}
以上代码,因为IsLeft不是静态方法,所以要实例化所在的类,然后通过实例对象调用IsLeft
3.给方法传递参数
参数可以通过引用或通过值传递给方法。
例子:
参数是值类型还是引用类型的区别
static void Main(string[] args)
{
int _i = 0;
int[] _ints = { 0 };
Console.WriteLine("运行方法前:_ints[0]={0},_i={1}", _ints[0], _i);
SomeFunction(_ints, _i);
Console.WriteLine("运行方法后:_ints[0]={0},_i={1}", _ints[0], _i);
Console.ReadKey();
}
static void SomeFunction(int[] ints, int i)
{
ints[0] = 100;
i = 100;
}
运行以上代码,结果如下:

4.ref参数
输入参数前带有 ref 关键字(使用 ref 的参数必须初始化),则该方法对变量所做的任何改变都会影响原始对象的值
static void Main(string[] args)
{
int _i = 0;
int[] _ints = { 0 };
Console.WriteLine("运行方法前:_ints[0]={0},_i={1}", _ints[0], _i);
SomeFunction(_ints,ref _i);
Console.WriteLine("运行方法后:_ints[0]={0},_i={1}", _ints[0], _i);
Console.ReadKey();
}
static void SomeFunction(int[] ints,ref int i)
{
ints[0] = 100;
i = 100;
}
运行以上代码,结果如下:

5.out参数
输入参数前面加上 out 关键字(可以不初始化),方法里把要输出的值直接赋给这个参数即可
static void Main(string[] args)
{
int _i;
SomeFunction(out _i);
Console.WriteLine("运行方法后:_i={0}", _i);
Console.ReadKey();
}
static void SomeFunction(out int i)
{
i = 100;
}
运行以上代码,结果如下:

6.命名参数
命名参数允许按任意顺序传递。
static void Main(string[] args)
{
FillName("张","三");
FillName(lastName:"三",firstName:"张");
Console.ReadKey();
}
static void FillName(string firstName, string lastName)
{
Console.WriteLine(firstName + " " + lastName);
}
运行以上代码,结果如下:

7.可选参数
当为参数提供了默认值时,参数就可选了。可选参数必须定义在参数列表的最后面
例子:
static void FillName(string firstName, string lastName="三")
{
Console.WriteLine(firstName + " " + lastName);
}
8.方法的重载
C#支持方法的重载——方法的几个版本有不同的签名(即,方法名相同,但参数的个数或类型不同)。
static void Show(string value)
{
Console.WriteLine(value);
}
static void Show(int value)
{
Console.WriteLine(value);
}
(2)属性
属性的概念是:它是一个方法或一对方法,在客户端代码看来,它们是一个字段。
例子:
以下代码定义了一个名为Age的属性
public int age;
public int Age
{
get
{
return age;
}
set
{
ageage = value;
}
}
我们采用C#区分大写的模式,使用相同的名称,但公有属性采用Pascal大小写形式命名,如果存在一个等价的私有字段,则它采用camel大小写形式命名。
1.只读和只写属性
在属性定义中省略set访问器,就可以创建只读属性;省略get访问器,就可以创建只写属性(一般不创建只写属性)
public int age;
public int Age
{
get
{
return age;
}
}
2.属性的访问修饰符
C#允许给属性的get和set访问器设置不同的访问修饰符,但必须有一个具备属性的访问级别。
public int age;
public int Age
{
get
{
return age;
}
private set
{
age = value;
}
}
3.自动实现的属性
如果属性的set和get访问器中没有任何逻辑,就可以使用自动实现的属性。
例子:
public int Age { get; set; }
使用自动实现的属性,就不能再属性设置中验证属性的有效性。虽然不能删掉get或set访问器,单可以给它们设置访问级别。
例子:
public int Age { get; private set; }
(3)构造函数
声明基本构造函数的语法就是声明一个与包含的类同名的方法,但是该方法没有返回类型。
例子:
以下代码,创建了一个名为People的类,然后在其中创建了其无参的构造函数(当我们不提供任何构造函数时,编译器会在后台默认创建一个无参构造函数)
class People
{
public People()
{ }
}
以下代码,创建了一个有参数的构造函数,这个时候编译器就不会默认创建一个无参构造函数
class People
{
string name;
public People(string Name)
{
name = Name;
}
}
1.静态构造函数
静态构造函数没有参数,只执行一次,且一个类只能有一个静态构造函数,而前面的构造函数都是实例构造函数,只要创建类的对象就会执行它。
例子:
以下代码创建People类的静态构造函数
class People
{
static People()
{
}
}
2.构造函数中调用其他构造函数
使用 this 关键字调用同一类中的其他构造函数
class People
{
public People(string firstName, string lastName)
{
Console.WriteLine(firstName + " " + lastName);
}
public People(string lastName):this("张",lastName)
{ }
}
使用 base 关键字可以调用基类中的构造函数
class PeopleBase
{
public PeopleBase(string firstName, string lastName)
{
Console.WriteLine(firstName + " " + lastName);
}
}
class People: PeopleBase
{
public People(string lastName):base("张",lastName)
{ }
}
3、只读字段
readonly关键字比const灵活得多,允许把一个字段设置为常量,但还需要执行一些计算,已确定它的初始值。其规则是可以在构造函数中给只读字段赋值,但不能在其他地方赋值。
例子:
class People
{
public readonly string Country;
public People(string firstName)
{
if (firstName == "约翰")
{
Country = "外国";
}
else if (firstName == "张")
{ Country = "中国";
}
}
}
(三)匿名类型
var与new关键字一起使用时,可以创建匿名类型。
例子:
以下代码创建了一个包含个人姓氏、名字和年龄的对象
var Person = new { firstName = "张", lastName = "三", age = "3" };
这个对象的类型名是编译器赋的一个我们无法使用名称,我们也不应该在类型名上去进行其他操作,因为会造成结果不同。
(四)结构
定义结构我们只需要用 struct 关键字替换类的 class 关键字即可
例子:
struct People
{
public string Name;
}
需要注意:
结构是值类型。
结构不支持继承。
结构的无参构造函数总是由编译器提供,不可替换。
使用结构,可以指定字段如何在内存中布局
1、结构是值类型
虽然结构是值类型,但在语法上完全可以把它当做类来处理。但也因为结构是值类型,new 运算符与类和其他引用类型的工作方式不同,结构的new运算符并不分配堆中的内存,而是只调用相应的构造函数,根据传送给它的参数,初始化所有的字段。
例子:
以下代码完全合法(假定People在这里是结构)
People people;
people.Name = "张三";
结构遵循其他数据类型都遵循的规则:在使用前所有的元素都必须进行初始化。
在结构上调用new运算符,或者给所有字段分别赋值,结构就完全初始化了。
结构主要用于小的数据结构。
2、结构和继承
结构不是为继承设计的。这意味着:它不能从一个结构中继承。每个结构都派生自ValueType。
3、结构的构造函数
为结构定义构造函数的方式与类相同,唯一的区别是结构不允许定义无参的构造函数。结构的无参构造函数有编译器提供,其作用是把数值初始化为0,引用类型初始化为null,即使提供了其他构造函数也是如此。
(五)弱引用
在应用程序代码内实例化一个类或结构时,只要有代码引用它,就会形成强引用。弱引用是使用WeakReference类创建的。
例子:
1 static void Main(string[] args)
2 {
3 WeakReference peopleReference = new WeakReference(new People());
4 People people;
5 if (peopleReference.IsAlive)
6 {
7 people = peopleReference.Target as People;
8 people.Name = "张三";
9 people.Age =12;
10 Console.WriteLine("{0}今年{1}岁",people.Name,people.Age);
11 }
12 else
13 {
14 Console.WriteLine("引用为空");
15 }
16 GC.Collect();
17 if (peopleReference.IsAlive)
18 {
19 people = peopleReference.Target as People;
20 }
21 else
22 {
23 Console.WriteLine("引用为空");
24 }
25 Console.ReadKey();
26 }
(六)部分类
partial关键字允许把类、结构、方法或接口放在多个文件中。
partial class People
{
public string Name;
public int Age;
} partial class People
{
public void Show() {
Console.WriteLine("{0}今年{1}岁", this.Name, this.Age);
}
}
(七)静态类
如果类只包含静态的方法和属性,该类就是静态的。给类添加 static 关键字,编译器可以检查用户是否不经意间给该类添加了实例成员。
例子:
静态类的语法
static class People
{
public static string Name;
public static int Age;
public static void Show() {
Console.WriteLine("{0}今年{1}岁", Name, Age);
}
}
例子:
调用上面的静态类
static void Main(string[] args)
{
People.Name = "张三";
People.Age = 18;
People.Show();
Console.ReadKey();
}
(八)Object类
所有的.NET类都派生自System.Object。
1、System.Object()方法
- ToString()方法:是获取对象的字符串表示的一种便捷方式。
- GetHashCode()方法:如果对象放在名为映射(也称为散射或字典)的数据结构中,就可以使用这个方法。
- Equals()方法和ReferenceEquals()方法:用于比较对象相等性。
- Finalize()方法:Object中的Finalize()方法其实什么也没做。
- GetType()方法:这个方法返回从System.Type派生的类的一个实例。这个对象可以提供对象成员所属类的更多信息,包括基本类型、方法、属性等。System.Type还提供了.NET反射的入口点。
- MemberwiseClone()方法:复制对象,但只是一个浅表复制,即复制了所有的值,但引用对象赋值的是引用地址。
2、ToString()方法
它是快速获取对象的字符串表示的最便捷的方式。
例子:
static void Main(string[] args)
{
int i = 100;
string str = i.ToString();
Console.ReadKey();
}
运行以上代码,结果如下:

Obejct.ToString()声明为虚方法,所以可以在类中重写该方法
例子:
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 People people = new People();
6 people.Name = "张三";
7 people.Age = 18;
8 Console.WriteLine(people.ToString());
9 Console.ReadKey();
10 }
11 }
12
13
14 class People
15 {
16 public string Name;
17 public int Age;
18 public override string ToString()
19 {
20 return string.Format("{0}今年{1}岁", this.Name, this.Age);
21 }
22 }
运行以上代码,结果如下:

(九)扩展方法
如果有类的源代码,继承就是给对象添加功能的好方法。但如果没有源码,则可以使用扩展方法,它允许改变一个类,但不需要该类的源代码。
扩展方法是静态的,它是类的一部分,但实际上没有放在类的源代码中。
例子:
以下代码创建了People类的一个扩展方法
static class PeopleExtension
{
public static string AddCountryToPeople(this People people, string country)
{
return people.ToString() + "居住在" + country;
}
}
调用以上扩展方法
static void Main(string[] args)
{
People people = new People();
people.Name = "张三";
people.Age = 18;
Console.WriteLine(people.AddCountryToString("中国"));
Console.ReadKey();
}
运行以上代码,结果如下:

需要注意:
- 调用扩展方法需要使用对象的实例。
- 如果扩展方法名与源代码中的某个类名重复,那么扩展方法无效。
【读书笔记】C#高级编程 第三章 对象和类型的更多相关文章
- 读书笔记 - js高级程序设计 - 第三章 基本概念
启用严格模式 "use strict" 这是一个 pragma 编译指示 让编码意图更清晰 是一个重要原则 5种简单数据类型 Undefined Null Boolean Num ...
- c#高级编程第七版 学习笔记 第三章 对象和类型
第三章 对象和类型 本章的内容: 类和结构的区别 类成员 按值和按引用传送参数 方法重载 构造函数和静态构造函数 只读字段 部分类 静态类 Object类,其他类型都从该类派生而来 3.1 类和结构 ...
- 读书笔记 - js高级程序设计 - 第六章 面向对象的程序设计
EcmaScript有两种属性 数据属性 和 访问器属性 数据属性有4个特性 Configurable Enumerable Writable Value 前三个值的默认值都为false ...
- unix环境高级编程第三章笔记
文件描述符 1.文件描述符的概念 对于内核而言,所有打开的文件都会用一个文件描述符来引用,打开或和创建一个新文件的时候,内核会给进程返回一个文件描述符,而当使用read write时,可以使用这个文件 ...
- 【读书笔记】C#高级编程 第七章 运算符和类型强制转换
(一)运算符 类别 运算符 算术运算符 + - * / % 逻辑运算符 & | ^ ~ && || ! 字符串连接运算符 + 增量和减量运算符 ++ -- 移位运算符 < ...
- 读书笔记-JavaScript面向对象编程(三)
第7章 浏览器环境 7.1 在HTML页面中引入JavaScript代码 7.2概述BOM与DOM(页面以外事物对象和当前页面对象) 7.3 BOM 7.3.1 window对象再探(所以JavaSc ...
- UNIX环境高级编程-第三章习题
1,当读写磁盘文件时,read,write等函数确实是不带缓冲机制的吗?请说明原因. 答:所有磁盘I/O都要经过内核的块缓存区(即内核的缓冲区高速缓存).唯一例外的是对原始磁盘设备的I/O,但是我们不 ...
- 读书笔记 - js高级程序设计 - 第五章 引用类型
引用类型 和 类 不是一个概念 用typeof来检测属性是否存在 typeof args.name == "string" 需要实验 访问属性的方法 .号和[] 一般情况下要 ...
- 读书笔记 - js高级程序设计 - 第十一章 DOM扩展
对DOM的两个主要的扩展 Selectors API HTML5 Element Traversal 元素遍历规范 querySelector var body = document.query ...
随机推荐
- UiPath存在文本Text Exists的介绍和使用
一.Text Exists的介绍 检查是否在给定的UI元素中找到了文本,输出的是一个布尔值 二.Text Exists在UiPath中的使用 1. 打开设计器,在设计库中新建一个Sequence,为序 ...
- 『现学现忘』Docker基础 — 42、补充:save和load命令说明
目录 1.save命令 2.load命令 1.save命令 将指定的一个或多个镜像保存成.tar格式的文件,进行打包归档. 查看docker save帮助命令,如下: [root@192 ~]# do ...
- Python实现中文字幕雨+源代码
写在前面的一些P话: 最近浏览了很多关于用Python和Pygame实现代码雨的案例,发现很多都是没有深入讲解代码的整个实现过程,从0到1教会你制作中文文字雨. 然后在介绍的过程中,我也将深入介绍Py ...
- 抓到 Netty 一个 Bug,顺带来透彻地聊一下 Netty 是如何高效接收网络连接的
本系列Netty源码解析文章基于 4.1.56.Final版本 对于一个高性能网络通讯框架来说,最最重要也是最核心的工作就是如何高效的接收客户端连接,这就好比我们开了一个饭店,那么迎接客人就是饭店最重 ...
- 如何用WebGPU流畅渲染百万级2D物体?
大家好~本文使用WebGPU和光线追踪算法,从0开始实现和逐步优化Demo,展示了从渲染500个2D物体都吃力到流畅渲染4百万个2D物体的优化过程和思路 目录 需求 成果 1.选择渲染的算法 2.实现 ...
- Gauss 消元法
目录 1. 线性方程组 2. 球形空间产生器sphere 3. 臭气弹 4. 开关问题 错乱瞎写 1. 线性方程组 省流:初等行变换化为一个上三角,然后瞬间出解 inline bool z(const ...
- Linux上安装java
1,输入命令,查看是否已经安装了Openjdk:rpm -qa | grep java 如果有已经安装的java版本或者版本低于1.7,卸载该jdk:rpm -e 软件包名字 如果不能卸载,可以加上 ...
- PHP memcache add replace set的区别和其他用法收集
add replace set的区别 最近在面试时遇到一个问题 memcache 的add replace set的区别,故在此进行加强 add 是向服务器添加一个缓存的数据,当该键已存在会返回一个f ...
- 转一篇MYSQL文章《数据库表设计,没有最好只有最适合》
http://mp.weixin.qq.com/s/a8klpzM5iam0_JYSw7-U4g 我们在设计数据库的时候,是否会突破常规,找到最适合自己需求的设计方案,下面来举个例子: 常用的邻接表设 ...
- pat甲级考试+pat1051+1056
同上一篇博客: 贪心题目我已经刷了将近30道了,由于那几天考驾照就没写,以后有空的时候补过来吧,都在codeblock里 pat的题也刷了点,acwing 的题也刷了点,基本都攒下了.以后也会慢慢补过 ...