唠唠嗑
嗨,我是小白,如果你不喜欢看一整本书而是带着轻松的心情看别人学习一本书,或者喜欢通过和人讨论的模式来进行学习,就来关注我吧。 如果有比我还小白的小白们有非常非常基础的问题要问,我会根据私信人数出期特别的,简单易懂的教程哦。
最近在成都拿到一个U3D开发的offer,面试的时候给人感觉是很好的公司,让处于迷茫期的我有了实际的第一份工作,想了很久还是做游戏能够激发我的学习热情呀,加油!
这本书《C#图解教程》一共二十六章,计划九月前读完! 系列文章会记录我认为最有价值的内容哦。 不过都是根据我的理解加工过的内容,可能会有那么百分之二的部分有根本上的错误,也有可能会因为人与人看待事物的差异而导致你理解错我的意思,所以建议先对我的话有想法,然后去利用搜索引擎或者官方文档提供的信息进行验证哦。 也可以和我 深 入 交 流 哦。
总目录
这是这本书的目录( ̄▽ ̄)~*
本文章阅读目录
一、.NET
虽说这本书叫C#图解教程,但在学习C#之前,了解一下它所属的框架以及它进行工作的原理会更好。
1. 这个框架(.NET)是什么?
首先,我要讲的这个是微软的一个产品,而不是一个类似.com的域名(.net)。 它是一个框架,用来开发你想要的东西的框架,我之前对它的认识是一门语言,还和visual studio挂钩,但看完这本书,我发现我之前这种认识也不是没有道理。 鉴于我之前使用过JAVA,用JAVA打个比方的话,.NET和JAVA非常相像。
你看,JAVA的工作原理是什么?你写完一个.java文件,先编译成字节码,然后放到JVM里解释成机器码执行,然后程序就跑起来了,对吧。
.NET的工作原理也是这个先编译成某个文件再放到某个东西里执行的过程,比如你要使用C#,编写好一个源代码文件,先经过C#语言编译器编译成一个叫做程序集的东西,,程序集的主要内容是CIL,它的中文名称叫做公共中间语言(都有中间俩字了,你肯定明白它不是最终执行的东西),然后程序集由一个叫做CLR(你可以把它理解成JVM)的东西编译(这里我不知道能不能称作解释,但是它是运行时编译)成本机代码(机器码),然后程序就跑起来了。
它和JVM不同的地方在于,JVM有个热点代码检测的东西,一段代码如果总是被调用,调用到一定次数的话那编译内容就会储存到内存里,以后使用这段代码直接从内存提取编译完成的本机代码而不是每次都解释执行,而CLR是只要一段代码被用到了,被编译了,那以后也不会再编译了,因为编译内容会被保存下来以后直接使用。 甚至还有一种叫Ngen的工具,你可以把程序集全部一次变成可运行的本机代码,这意味着通过.NET你不仅拥有了解释型语言的跨平台性,也拥有了编译型语言的高效率,个人分析.NET比JAVA的效率会高很多,因为CLR的只编译一遍的特性,也就第一次会比JAVA慢点。如果有小伙伴看不懂我这段话,可以看我的这篇文章: 小白也能看懂,编译型语言与解释型语言到底是什么?原理类(一).
.NET还有一些特性,面向对象,GC机制,都和JAVA很像。
只是,.NET支持的语言很多,C#,F#,VB.NET,python等等都可以应用它这个框架。说到python,我还用过Pycharm,我想直接用python解释执行和.NET编译运行python源文件的最主要的区别在于后者效率更高吧。不过听说JVM也不止支持JAVA这一种语言。
2. 程序集是什么?
.NET的程序集,要么是可执行文件,要么是DLL文件。 DLL...突然想起来小时候玩游戏,弹出一个缺少XXX.DLL然后导致游戏打不开或强制关闭的恐怖回忆(;´д`)ゞ。 小时候不知道为啥会自动关闭了,现在懂了,因为程序运行到这少了段代码,那肯定要么卡住不动,要么被异常处理的逻辑给自动关闭了啊。
回到正题,那么程序集是什么呢,它包含了三样东西。
- 程序的CIL(有时也被称作IL,反正就是中间语言);
- 程序中使用的类型的元数据;
- 对其他程序集引用的元数据。
程序集处于中间的一个部件,请看这张图:
各个不同的语言通过各自的编译器先不编译成最终可以在机器上运行的本地代码,而是编译成共通的程序集,最后只要是有.NET的计算机就可以通过把程序集编译成本机代码来达到跨平台的目的。
但是……当然,CLR作为.NET的核心构件,也是有极限的,例如一些十几年前编写的代码可能就没法被CLR管理(CLR管理是指程序在CLR上执行,异常处理,GC处理等等),这种代码被称作非托管代码。
3. 托管代码与非托管代码
简单来讲,被CLR管理的代码就是托管代码,不被其管理的就是非托管代码,所以非托管代码不会享受到CLR的服务。
而CLR的服务包括这些:
- 内存管理和垃圾收集
- 线程管理及异常处理
- 代码安全验证
所以非托管代码通常需要加上你自己编写或ctrl+v的这些功能(〃'▽'〃)。
C#编写的代码都是托管代码。即使你用Ngen跳过JIT编译而把C#代码直接全部编译为本机代码,之后运行依然会被CLR托管。我这么想的依据是:
来源于:cn.voidcc.com/question/p-…
那什么情况会产生非托管代码呢?
- Visual Studio .NET 2002发布之前所创建的代码
- Visual C++的用户通过自己的意识创建的非托管代码
至于C++怎么创建非托管代码,呃,我不想了解,送上一篇介绍了更详细非托管代码的信息的文章链接,里面有写到: blog.csdn.net/birdflyto20…
二、C#编程概述
1. 程序的起始点
先按照惯例,一个简单的Hello World:
1 using System;
2 namespace Simple
3 {
4 class Program{
5 static void Main(){
6 Console.WriteLine("Hello World!");
7 }
8 }
9 }
C#程序要求必须有一个类带有Main()函数,该函数会成为程序的入口。 所以这里,运行了System.Console.WriteLine()这个函数,System在哪呢?在第一行,这个using关键字起到了作用,让你可以在该文件下调用using后面标注的命名空间的函数时,都可以省略 命名空间.类.函数 形式中的 命名空间. 这个内容。
第二行的namespace自不必说,就是命名空间的意思。
这个文件编好后,可以通过在命令提示符里跳到程序文件所在目录输入命令csc XXX.cs文件编译c#文件。
可以思考一下编译后会产生什么东西(这个东西能否直接让机器执行),编译包含Main()的文件就相当于编译整个工程了吗,还是说,单单编译包含Main()的文件会导致异常?
如果能思考一下一定对你的编程思维有帮助哦φ(>ω<*) !
其实我也不太清楚答案,打算以后回过头看自己笔记时再搜寻网络上的信息。
2. C#标识符规范
除此以外还有一些注意点:
- 空白(空格符,回车符,制表符,换行符)在源代码里显示虽然是空白,但经过编译器时会被忽略。
- 函数名和类名一样,采用Pascal大小写,即首字母大写,之后每个单词的首字母大写,其余小写,这和java不一样。
- 关键字不能作为标识符。
三、替代标记
先给出一个实例:
System.Console.Write("1+1={0}",2};
其中这个括住0的大括号就是替代标记,它存在于字符串中,使用了它的字符串被称作格式字符串。
它的作用很简单,就是把后面的那个2与字符串中的替代标记替换。 那你可能要问了,直接大括号不行吗,里面括个0啥意思。 这个0,是替换列表的索引。 替换列表是什么?啊啊,抱歉我应该给一个例子你才能更好地理解。
System.Console.Write("1+1={0}还是={1}",1,2);
格式字符串后面的参数列表,就是数个参数的集合就是替换列表,这里的情况是1,2,那么{0}和{1}就分别是1和2 。
有很多聪明的人可能我还没说就知道了,甚至还会嫌我啰嗦,但下面的信息如何呢,这是使用替换标记时需要注意的地方:
- 替换标记不限次数使用。
- 替换标记不需要按顺序来。
- 替换标记的索引不能超过替换列表的长度。
再给出一个例子:
System.Console.Write("1+1可以等于{1},
也可以等于{0},但我还是倾向于这个东西等于{1}",2,1};
等等,边写边发现了一些问题,C#到底允不允许一行语句多行编写呢!!!∑(゚Д゚ノ)ノ
先不管那个,这行语句输出的字符串是1+1可以等于1, 也可以等于2,但我还是倾向于这个东西等于1。
看到了吧,{1}可以放到{0}前面,而且不限使用次数。 但真正要注意的是索引不能超过替换列表的长度,一旦出现这个问题,意味着你只能在运行编译完的程序才能发现这个问题,因为出现这种问题,编译时是不会报异常的,运行时才会。
System.Console.Write("1+1可以等于{1},
也可以等于{0},但我还是倾向于这个东西等于{1},因为我是{2}",2,1};
这种情况就是有问题的情况,因为{2}没有能够替换的东西,格式字符串后面只有两个参数,而{2}调用的是第三个参数,这里没有第三个参数。
另外替代标记还有个格式符,通过输入格式符可以让替代标记有更多的效果我来举个栗子:
System.Console.Write("“我有{0:C}的钱”,10000};
会输出:我有$10000.00的钱。 :C是将值格式为货币的意思,其他还有种种:
另外你也可以通过替代标记来设置替代的字符长度:
四、类型
1. 预定义类型
这个没什么好讲的呢,就是单纯的简单易记的东西,重点在后面。
基本的预定义类型
用户定义类型
预定义类型一开始就在,不需要你定义,就可以实例化一个对象。 而用户定义类型需要你先定义声明才可以实例化它的对象。 它分这么几种:
- 类类型(class)(真是拗口)
- 结构类型(struct)
- 数组类型(array)
- 枚举类型(enum)
- 委托类型(delegate)
- 接口类型(interface)
委托类型和结构类型我是真不知道到底干啥的,不过这本书都有关于它们的信息,到时候补全这部分知识我应该也写出关于它们的文章了o(´^`)o
接下来的才是重点
2.栈和堆
在C#中,程序运行时必须要将数据存储在内存里,而内存又分两个区域,栈和堆。
栈的特征
首先这是它的结构:
栈里的数据要取出来你只能从顶端开始,取数据的操作被称为出栈。存也一样,放上去的数据就在顶端,存数据的操作被称为入栈。
堆的特征
堆就没有那么多规矩了,随便放数据,如果这个数据被GC判断没用了就会被删掉:
3.值类型与引用类型
这是从另一种角度对数据进行的分类。之所以先了解栈和堆的概念,就是为了解说这两个分类。
首先给出一个结论: 值传递仅仅传递的是值,不影响原始值。引用传递,传递的是内存地址,修改后会改变内存地址对应储存的值。
其实写的时候我突然发觉理解还不够透彻又查了几篇文章(´・ᴗ・`)
为什么会这样? 这就得从栈这个结构说起了,我们的程序在执行语句时,是把语句依次执行然后入栈,当当前的语句调用一个函数时,会把函数的执行语句入栈,然后再把传入该函数的参数入栈。
这个参数,如果是值类型,就会在栈中被完全拷贝出另一个副本,在该函数里就对这个副本进行操作,不会影响到原本那个变量的值。
但如果是引用类型,拷贝的就只是变量的引用,引用类型的变量使用的内存分栈和堆,它的引用放在栈里,但真实的数据放在堆里。所以传入的参数如果是引用类型,对该参数的操作会影响到堆里存放的数据。
这里有一篇解释的很好的文章: blog.csdn.net/qq_42194657…
另外,还有一件值得注意的事: 当值类型变量为一个类里的字段(成员变量)时,那它们作为这个类的对象的数据,也会被放到堆里。
那在C#中,哪些是值类型,哪些是引用类型的?
值类型:
- 除object,string,dynamic以外的预定义类型
- 一部分用户定义类型(struct,enum)
引用类型:
- object,string,dynamic
- 一部分用户定义类型(class,interface,delegate,array)
小结
内容还是挺多的,需要多多回顾。