《C#图解教程》(第4版)学习笔记
第4章 类型、存储和变量
1、C#程序是一组类型声明;(程序源代码的特征)
C程序是一组函数和数据类型;
C++程序是一组函数和类。
2、类型是一种模板
学习C#就是学习如何创建和使用类型。
- 类型是什么:类型是创建数据结构的模板。模板不是数据结构,而是数据结构的描述(它详细说明了由该模板构造的对象的特征)。
- 类型的定义:
- 实例化类型:从某个类型模板创建实际的对象,称为实例化该类型。
(1)通过实例化类型而对象被称为类型的对象或实例。这两个术语可以互换。
(2)在C#程序中,每个数据项都是某个类型的实例。这些类型可以是语言自代的、可以是BCL或其它库提供的、也可以是程序员定义的。 - 数据成员和函数成员
简单类型只能存储一个数据项,数组(array)类型可以存储多个同类型的数据项,而另外一些类型可以包含许多不同类型的数据项。
(1)简单类型:只能存储一个数据项。像short,int,long等
(2)数组(array)类型可以存储多个同类型的数据项。这些数据项称为数组的元素。
(3)其它类型:包含许多不同类型数据项的类型
其中有两种成员:
(1)数据成员:保存了与这个类的对象或整个类相关的数据
(2)函数成员:执行代码。其定义类型的行为。
例:图4-3列出了类型XYZ的两类成员,它包含了两个数据成员和两个函数成员:
5.预定义类型
C#提供16种预定义类型,如图4-4,包括13种简单类型和3种非简单类型。
所有预定义类型的名称都由全小写的字母组成。
简单类型包括以下3种:
(1)11种数值类型
【1】有符号数无符号数
【2】浮点类型float和double
【3】一种称为decimal的高精度小数类型,与float和double不同,该类型可以准确表示分数,所以decimal类型常用于货币计算。
(2)一种unicode字符类型char
(3)一种布尔类型bool
注意:与C和C++不同,在C#中的数值类型不具有布尔意义。
3种非简单类型:
(1)string,它是一个unicode字符数组;
(2)object,它是所有其它类型的基类;
(3)dynamic,使用动态语言编写的程序集时使用。
6.用户定义类型
人类可以很容易分清数字和字符的区别,但计算机不能区别,除非你明确告诉它1是数字,“汉”是文字,否则计算机分不清1和“汉”的区别。所以,在每个编程语言里都会有一个叫数据类型的东东。其实,就是对常用各种数据类型进行明确划分,你想让计算机进行数值运算,就传数字给它。你想让他处理文字,就传字符串类型给他。
除了c#提供的16种预定义类型,还有6种类型可以由用户自己创建:
(1)类类型(class) (2)结构类型(struct)
(3)数组类型(array) (4)枚举类型(enum)
(5)委托类型(delegate) (6)接口类型(interface)
类型通过类型声明创建,类型声明包括:
(1)要创建的类型各类;
(2)新类型名称;
(3)对类型中每个成员的声明(名称和规格)array和delegate类型除外,它们不含有命名成员。
一旦声明了类型,就可以创建和使用这种类型的对象,就像它们是预定义类型一样。
预定义类型只需进行实例化;用户定义类型需要两步:声明和实例化。
7.栈和堆
程序运行时,它的数据必须存储在内存中。一个数据项需要多大内存,存储在什么地方,以及如何存储都依赖于该数据项的类型。运行中的程序使用两个区域来存储数据项:栈和堆。
(1)栈是一个内存数组,是一个LIFO(后进先出)的数据结构。数据只能从栈顶插入和删除。
(2)堆:是块内存区域,在堆里可以分配大块内存用于存储某种类型的数据对象
8.值类型和引用类型
类型被分为两种:值类型和引用类型。
(1)值类型:数据存储在栈里,只需要一段单独的内存,用于存储实际的数据。
(2)引用类型:实际数据存放在堆里而引用存放栈里。(类型决定了对象在内存中存放的位置)需要两段内存
【1】第一段存储实际数据,它总是位于堆中
【2】第二段是一个引用,指向数据在堆中的存放位置
C#中? 、?? 、?. 、??= 的用法和说明
一、可空类型修饰符 ?
引用类型能用空引用来表示一个不存在的值,但是值类型不能。例如:
string str = null; //引用类型不会报错
int i = null; //编译报错
为了使值类型也能使用可空类型,就可以用 " ? "来表示,表现形式为"T?"。例如:
int? i //表示可空的整型
DateTime? time //表示可空的时间
语法 T? 是 Nullable<T>的简写。
Nullable<int> i = null;
等价于
int? i = null;
在Nullable赋值给非Nullable时,可采用下面的方法:
int? i = 10; int j = i??0;
意为
if(i.HasValue) { j=i.value; } else { j=0; }
结果为:10
二、空合并运算符 ??
用于定义引用类型和可空类型的默认值。如果此运算符的左操作数不为Null,则此操作符将返回左操作数,否则返回右操作数。
var c = a??b //当a不为null时返回a,为null时返回b
三、 ?.—null条件操作符(null-condition operator)
不为null时执行后面的操作。例如:
A?.Invoke("Hehe");
上述示例等价于
if(A == null)
{
//不执行
}
else
{
A.Invoke("Hehe");
}
例2:
private bool IsOk(List<int> rooms)
{
return rooms?.Count > 0;
}
等价于:
private bool IsOk(List<int> rooms)
{
if (rooms != null)
{
return rooms.Count > 0;
}
return false;
}
"?:"三元条件运算符
//"?:"三元条件运算符 is this condition true ? yes : no
string GetWeatherDisplay(double tempInCelsius) =>tempInCelsius<20.0?"Cold.":"Perfect!";
Console.WriteLine(time);
Console.WriteLine(GetWeatherDisplay(15)); //output:Cold.
Console.WriteLine(GetWeatherDisplay(25)); //output:Perfect!
四、 ??=
C# 8.0 引入了 null 合并赋值运算符 ??=。 仅当左操作数计算为 null 时,才能使用运算符 ??= 将其右操作数的值分配给左操作数。
List<int> numbers = null;
int? i = null;
numbers ??= new List<int>();
numbers.Add(i ??= 17);
numbers.Add(i ??= 20);
Console.WriteLine(string.Join(" ", numbers)); // output: 17 17
Console.WriteLine(i); // output: 17
类、属性、方法和字段
一、相关概念:
1、对象:现实世界中的实体(世间万物皆对象)
2、类:具有相似属性和方法的对象的集合
3、面向对象程序设计的特点:封装 继承 多态
4、对象的三要素:属性(对象是什么)、方法(对象能做什么)、事件(对象如何响应)
☆相互关系:类是对象的抽象,对象是类的实例,类是一种抽象的分类,对象则是具体事物。
比如如果车是一个类,某个人的一辆奔驰车就是一个对象,车的颜色质量就是它的属性,启动、停止这些动作则可以定义为车的方法。
在C#中,类的成员有四种:字段、属性、方法和事件。
字段是在类中定义的变量(可以是基本类型或引用类型),一般来说字段都被定义为私有。
方法就是在类里面定义的函数。
属性的定义:
访问修饰符 类型 属性名{
get
{//代码段}
set
{//代码段}
}
从形式上看,属性的定义很像方法的定义,只是没有参数而已。
再来看属性的使用,使用属性时很像是使用共有字段。
可以给属性赋值,也可以直接使用属性的值。例如:
class MyClass {
public int MyInt //名为MyInt的公有属性
{ get
{ //代码段}
set
{ //代码段}
}
}
在程序中,可以这样来使用该属性
MyClass c = new MyClass();
c.MyInt = 100; //
Console.WriteLine(c.MyInt);
是不是很像使用字段呢。
其实在给属性赋值的时候是调用了属性的set块;而获取属性值的时候是调用了属性的get块。所以在属性的get块中需要有一个return语句,来返回特定的值,比如return一个私有字段的值。
当给属性赋值的时候,会调用属性的set块,一般在set块中,我们会将赋给属性的值再赋给某个特定的私有字段。那么,赋给属性的值存在什么地方呢?答案是存储在一个叫value的东西里面(固定写法,而且value是关键字),所以在set块中,一般是将value赋给某个字段。
例如:
class MyClass
{
private int myInt;
public int MyInt
{
get
{
return myInt;
}
set
{
myInt = value;
}
}
}
// 在程序中使用属性来操纵私有字段
MyInt = 100;
int m = MyInt + 1;
在属性的get和set块中,我们可以做更多的操作,如:检查范围,格式转换等。
属性可以是public的、private的、protected的,也可以是virtual的、override的、abstract的。
属性的get和set块也可是是public的、protected的、private的或者默认的。
get和set块可以两个都有,也可以只有一个,但至少要有一个。
在类的内部使用属性时,和使用字段是一样的。
get块和set块又叫访问器,访问器的可访问性不可高于属性。
二、静态类与静态类的成员
静态类与非静态类基本上是相同的,但有一点明显不同:静态类无法被实例化。换言之,你无法使用new操作符来创建一个静态类的变量。因为它根本没有实例变量,所以你需要使用静态类的类名来访问静态类的成员。例如,如果你有一个名为UtilityClass的静态类,它有一个名为MethodA公有静态方法,你可以通过以下方式来调用该方法:
UtilityClass.MethodA();
静态类可用作操作一组方法的便捷容器
,这些方法只需关心操作输入的参数,不需要获取或设置任何内部实例字段。例如,在.NET类库中,静态的System.Math类包含了各种数学运算的方法,而不需要存储或检索Math类的特定实例所独有的数据。就是说,你可以通过指定类名和方法名来引用类的成员,就像下面示例一样:
double dub = -3.14;
Console.WriteLine(Math.Abs(dub));
Console.WriteLine(Math.Floor(dub));
Console.WriteLine(Math.Round(Math.Abs(dub)));
// Output:
// 3.14
// -4
// 3
实例成员应该通过实例去访问,静态成员应该通过类来访问。
三、结尾
静态类无法被实例化
静态类可用作操作一组方法的便捷容器(像Math那样,方便地将一大类的方法统一管理起来,而不是像C语言那样分散开来)
静态构造函数仅调用一次,静态类在程序所在的应用程序域的生命周期内一直保存在内存中
静态成员始终是通过类名而不是实例名来访问的
无论你创建了多少实例,静态成员只存在一个副本
声明时不含static修饰符时(const这种隐式静态的除外),它就被声明为了实例成员(实例成员有时也叫非静态成员)
Newtonsoft.Json实用特性(引用原文)
1.代码格式化
直接使用 JsonConvert.SerializeObject 的话,默认情况下所有的 json 是挤压在一块的,特别不方便阅读,JsonConvert 中提供了一个 Formatting.Indented 用来格式化 json,这样在 debug 的过程中就非常友好。
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
List<ReportModel> reportModel =new List<ReportModel>();
reportModel.Add(new ReportModel(){
ProductName = "法式小众设计感长裙气质显瘦纯白色仙女连衣裙",
TotalPayment = 99,
TotalCustomerCount = 2,
TotalProductCount = 333
});
ReportModel reportModel1 = new ReportModel() {
ProductName = "法式小众设计感长裙气质显瘦纯白色仙女连衣裙",
TotalPayment = 100,
TotalCustomerCount = 2,
TotalProductCount = 333
};
reportModel.Add(reportModel1);
reportModel.Add(new ReportModel() {
ProductName = "法式小众设计感长裙气质显瘦纯白色仙女连衣裙2",
TotalPayment = 100,
TotalCustomerCount = 3,
TotalProductCount = 444
});
//Formatting.Indented格式化 json,这样在 debug 的过程中就非常友好
var json = JsonConvert.SerializeObject(reportModel,Formatting.Indented);
Console.WriteLine(json);
}
}
public class ReportModel //定义一个报告数据结构类
{
public string ProductName { get; set; }
public int TotalCustomerCount { get; set; }
public decimal TotalPayment { get; set; }
public int TotalProductCount { get; set; }
}
结果如下:
2. 踢掉没有被赋值的字段
Netnewsoft 中提供了 DefaultValueHandling.Ignore 剔除默认值的枚举。
var reportModel = new ReportModel() { ProductName = "法式小众设计感长裙气质显瘦纯白色仙女连衣裙", TotalPayment = 100 };
var json = JsonConvert.SerializeObject(reportModel, Formatting.Indented, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore });
System.Console.WriteLine(json); }
3. 兼容其他语言的 驼峰,蛇形命名法
每一套编程语言都有各自偏好的命名法,比如 js 中都喜欢采用 驼峰命名法,在 mysql 中我见过最多的 蛇形命名法,而我们在 C# 中序列化的属性一般都是大写字母开头,比如你看到的 特性二 中的字段,那这里就存在问题了,有没有办法兼容一下,给 js 就用 驼峰,给 mysql 就用 蛇形,这样显得对别人友好一些 。
//驼峰命名 CamelCasePropertyNamesContractResolver
var json = JsonConvert.SerializeObject(reportModel, Formatting.Indented,new JsonSerializerSettings{ContractResolver = new CamelCasePropertyNamesContractResolver()});
//蛇形命名 SnakeCaseNamingStrategy
var json = JsonConvert.SerializeObject(reportModel, Formatting.Indented,new JsonSerializerSettings{ContractResolver = new DefaultContractResolver(){NamingStrategy = new SnakeCaseNamingStrategy()}});
4. 自定义属性的名字
所以这里面必然要存在一个 Mapping 的过程,这就可以用 JsonProperty -> propertyName 帮你搞定,为了方便演示,我还是用 reportModel 吧。
改造ReportyModel类:
public class ReportModel //定义一个报告数据结构类
{
[JsonProperty("产品名称")] public string ProductName { get; set; }
[JsonProperty("总客户数")] public int TotalCustomerCount { get; set; }
[JsonProperty("总付款")] public decimal TotalPayment { get; set; }
[JsonProperty("总产品数量")] public int TotalProductCount { get; set; }
}
运行结果:
5. 对字段的 正向剔除 和 反向剔除
//正向剔除:默认所有都显示,手工踢掉不显示的,
//使用 MemberSerialization.OptOut 配合 JsonIgnore
//本例只显示:ProductName
[JsonObject(MemberSerialization.OptOut)]public class ReportModel{
public string ProductName { get; set; }
[JsonIgnore] public int TotalCustomerCount { get; set; }
[JsonIgnore] public decimal TotalPayment { get; set; }
[JsonIgnore] public int TotalProductCount { get; set; }
}
//反向剔除:默认都不显示,手工指定要显示的,本例显示:ProductName
//使用 MemberSerialization.OptIn 配合 JsonProperty
[JsonObject(MemberSerialization.OptIn)]public class ReportModel{
[JsonProperty] public string ProductName { get; set; }
public int TotalCustomerCount { get; set; }
public decimal TotalPayment { get; set; }
public int TotalProductCount { get; set; }
}
6.多个 json 合并到 一个 Model
这个特性当初打破了我对 Newtonsoft 的认知观,不知道您呢?通常我们都会认为 一个 json 对应一个 model,一个 model 对应一个 json,居然还可以多个 json 对应一个 model 的情况,这就有意思了,场景大家可以自己想一想哈,这里使用 PopulateObject 方法就可以轻松帮你搞定,接下来看看怎么写这个代码:
var json1 = "{'ProductName':'法式小众设计感长裙气质显瘦纯白色仙女连衣裙'}";
var json2 = "{'TotalCustomerCount':1000,'TotalPayment':100.0,'TotalProductCount':10000}";
var reportModel = new ReportModel();
JsonConvert.PopulateObject(json1, reportModel);
JsonConvert.PopulateObject(json2, reportModel); }
如何在VS2019上传项目到GitHub?
GitHub是一个面向开源及私有软件项目的托管平台,因为只支持Git作为唯一的版本库格式进行托管,故名GitHub。
Git是什么?
直接记录快照,而非差异比较
Git是目前世界上最先进的分布式版本控制系统(没有之一)。
Git是一个开源的分布式版本控制系统,用以有效、高速的处理从很小到非常大的项目版本管理。[2] Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。
Torvalds 开始着手开发 Git 是为了作为一种过渡方案来替代 BitKeeper,后者之前一直是 Linux 内核开发人员在全球使用的主要源代码工具。
开放源码社区中的有些人觉得 BitKeeper 的许可证并不适合开放源码社区的工作,因此 Torvalds 决定着手研究许可证更为灵活的版本控制系统。尽管最初 Git 的开发是为了辅助 Linux 内核开发的过程,但是我们已经发现在很多其他自由软件项目中也使用了 Git。
Git有什么特点?简单来说就是:高端大气上档次!
那什么是版本控制系统?
如果你用Microsoft Word写过长篇大论,那你一定有这样的经历:
想删除一个段落,又怕将来想恢复找不回来怎么办?有办法,先把当前文件“另存为……”一个新的Word文件,再接着改,改到一定程度,再“另存为……”一个新文件,这样一直改下去,最后你的Word文档变成了这样:
过了一周,你想找回被删除的文字,但是已经记不清删除前保存在哪个文件里了,只好一个一个文件去找,真麻烦。
看着一堆乱七八糟的文件,想保留最新的一个,然后把其他的删掉,又怕哪天会用上,还不敢删,真郁闷。
更要命的是,有些部分需要你的财务同事帮助填写,于是你把文件Copy到U盘里给她(也可能通过Email发送一份给她),然后,你继续修改Word文件。一天后,同事再把Word文件传给你,此时,你必须想想,发给她之后到你收到她的文件期间,你作了哪些改动,得把你的改动和她的部分合并,真困难。
于是你想,如果有一个软件,不但能自动帮我记录每次文件的改动,还可以让同事协作编辑,这样就不用自己管理一堆类似的文件了,也不需要把文件传来传去。如果想查看某次改动,只需要在软件里瞄一眼就可以,岂不是很方便?
这个软件用起来就应该像这个样子,能记录每次文件的改动:
版本
用户
说明
日期
1 张三 删除了软件服务条款5 7/12 10:38
2 张三 增加了License人数限制 7/12 18:09
3 李四 财务部门调整了合同金额 7/13 9:51
4 张三 延长了免费升级周期 7/14 15:17
这样,你就结束了手动管理多个“版本”的史前时代,进入到版本控制的20世纪。
解决Visual Studio 2019 下载扩展非常慢的问题是
使用下载工具下载到本地,再安装
-
首先打开vs的管理扩展
-
在联机中找到想要下载的扩展,在右面栏中点击 详细信息
-
在网页中下载(推荐使用X雷等下载工具,会有P2P和镜像加速)
-
关闭VS,双击下载的文件安装即可
VS2019使用github
本人操作系统win10
GitHub for Visual Studio Documentation
第一步,下载vs github插件
直接看图
下载完成后,需要关闭所有vs2019窗口,之后会弹出下面的窗口,点击modify,完成之后,重新打开vs就安装好了
下面就可以在vs里登录github之后就可以使用了
用的是edge浏览器,登录之后就这样子
可以选择克隆或创建
演示创建
创建成功之后,github网站上就可以看到项目
然后在团队资源管理器,找到项目,然后在解决方案项中,新建项目
创建完成
然后在团队资源管理器,找到项目,然后在解决方案项中,新建项目
创建完成
在团队资源管理器,选择更改
填写说明,然后全部提交
提交之后,还要再同步里,选择推送(推送要翻墙?PS:没翻墙总是推送不成功,翻墙后一次推送成功)
然后在团队资源管理器,找到项目,然后在解决方案项中,新建项目
创建完成

在团队资源管理器,选择更改
填写说明,然后全部提交
提交之后,还要再同步里,选择推送(推送要翻墙?PS:没翻墙总是推送不成功,翻墙后一次推送成功)
刷新github网站已经可以看到提交的代码文件
至此,简单试用完成,想要熟练使用还要多练习使用
vs中提示弹窗给出了github for visual studio 的文档:VisualStudio/docs at master · github/VisualStudio · GitHub
C#学习笔记(十三)委托——一切皆地址
委托(delegate)是函数指针的“升级版”
委托:按照字面意思:一件事情,我不亲自去做,而是让别人去代办,替我去做。间接去完成事情。比如上文,定义了函数,自己不干所属的功能任务,让别人去干这事。(比如:让新线程去做这事)。
谁调用这个函数方法,谁干这事。
一切皆地址:
变量(数据)是以某个地址为起点的一段内存中所存储的值;
函数(算法)是以某个地址为起点的一段内存中所存储的一组机器语言指令。
程序的本质,就是:数据+算法。数据存储在变量中,或者说变量代表着数据。函数代表的是算法。
变量的本质: 是以变量名所对应的内存地址为起点的一段内存,内存中存储的就是变量的数据。这段内存有多大,是由变量的数据类型所决定的。所以说:变量是地址。
函数的本质,是以函数名所对应的内存地址为起点的一段内存,在这段内存中,存储的不是某个值,而是一组机器语言指令。CPU就是按照这组指令一条条的去执行,完成函数所包含的算法。
综上,无论是数据还是算法,都是保存在内存地址中的。
变量用来寻找数据的地址。
函数用来用来寻找算法的地址。
直接调用与间接调用
直接调用:通过函数名来调用函数:CPU通过函数名直接获得函数所在地址并开始执行➨➨➨返回调用者。
间接调用:通过函数指针来调用函数:CPU通过读取指向某个函数的函数指针存储的值获得函数所在地址并开始执行➨➨➨返回调用者。
直接调用和间接调用,效果是完全一样的。
Action和Function是CSharp中为我们准备好的委托。