本文是一个菜鸟所写,本文面向的人群就是像我这样的小菜鸟,工作一年也辛辛苦苦学习了一年,一直没有机会梳理一下自己的知识,最近花了一些时间整理了一些C#基础知识,也算是对过去的一年做个回顾把~ 文章有点长,请自带瓜子和茶吧,请看下面C#基础知识简单架构图,不可能100%的全面,请见谅啊... 1.值类型和引用类型 1.1堆和栈 简单的说值类型存放在堆栈上面,引用类型的数据存放在托管堆上面(它的引用地址却存放在堆栈上面)! 栈:它是一个内存数组,是一个先进后出的数据结构! 栈的特征:数据只能从栈顶进,从栈顶出! 堆:它是一个内存区域,可以分配大块区域存储某类型的数据,与栈不同的是它里面的数据可以任意排序和移除! 下面是园子的一张图,贴上来供大家参考啊! 问 题 | 值 类 型 | 引 用 类 型 | 这个类型分配在哪里? | 分配在栈上 | 分配在托管堆上 | 变量是怎么表示的? | 值类型变量是局部复制 | 引用类型变量指向被分配得实例所占的内存 | 基类型是什么? | 必须继承自System.ValueType | 可以继承自除了System.ValueType以外的任何类型,只要那个类型不是sealed的 | 这个类型能作为其他类型的基类吗? | 不能。值类型是密封的,不能被继承 | 是的。如果这个类型不是密封的,它可以作为其他类型的基类 | 默认的参数传递是什么? | 变量是按值传递的(也就是,一个变量的副本被传入被调用的函数) | 变量是按引用传递(例如,变量的地址传入被调用的函数) | 这个类型能重写System.Object.Finalize()吗? | 不能。值类型不好放在堆上,因此不需要被终结。 | 可以间接地重写 | 我可以为这个类型定义构造函数吗? | 是的,但是默认的构造函数被保留(也就是自定义构造函数必须全部带有参数) | 当然! | 这个类型的变量什么时候消亡? | 当它们越出定义的作用域时。 | 当托管堆被垃圾回收时。 |
1.2装箱和拆箱 这类的文章真的多了,再总结就没多大的意义了,看的时候多写写代码,多想想,就会明白的! 2.接口,抽象类,封装,继承,多态 接口和抽象类这两个概念还真不容易理解,有的时候理解一半,换一种方法考考你,你就会晕,到现在说实话我还没完全懂,一直没有把握它们的精髓,最近在看<<你必须知道的.NET>>,这是第二次看,收获很多... 大家还是有时间多看看<<你必须知道的.NET>>,这本书可以说是很详细的讲解了OO思想,还有看看设计模式的书,多想多练,可以时间会长一点,不过总有一点我们会开窍的... 这种东西不是通过总结一下就能熟练运用的,不过你起码要有一点面向对象的思想,要想有这种思想必须学习前辈留下的知识总结,这种才能理论结合实践,才能深入的了解OO思想 3.迭代器 4.泛型 泛型保证了类型安全,避免了装箱和拆箱的操作,提高了性能,可复用性也得到了很大的提高,下面就来说说基本的泛型语法吧! 项目中对于泛型和委托的结合运用也很多见,很多人不是为了语法而学习,而是泛型的扩展性让我们必须要知道它,把它实实在在的运用到项目中去,提高扩展性... 泛型语法不是很复杂,包括定义泛型类型,泛型方法,指定泛型约束,还有泛型约束包括只包括哪些类型等等,这些语法只要花些时间就能明白了,难的是一种思想,o(︶︿︶)o 我还很菜啊... 5.集合 5.1一般集合 .NET Frameword中关于集合的类存储在System.Collections命名空间下,其实一开始学习的时候感觉集合这个东西很神秘,能动态增加,删除,选择数据(比数据好用多了),可是在学习之后,它的神秘感也随之消息,因为集合的底层代码跟数组有着密切联系的,请看:学习之路二:关于集合和数组内在联系的深入了解(里面也有个链接,可以点击学习)! 下面是非泛型集合类之间的关系图: 5.2泛型集合 自从.NET Framework引用泛型概念之后,它在C#编程方面掀起了一个泛型热潮,泛型实在太好用了,不仅是类型安全,可扩展性,重要的是在性能方面有了显著提高,这让我们苦逼的程序猿看到了曙光,哈哈... 泛型集合类存储在System.Collections.Generic以及System.Collections.ObjectModel命名空间下,下面是集合类之间的关系图: 6.反射 反射这东西两面性很极端,很多人说它的坏,也有很它在某些方面有着重大的作用,下图是关于类型反射所需要用到的类之间的关系图: 除了类型反射之外,还有一种是程序集的反射,功能比较强大,可是我对它的研究比较少,我就推荐几篇好文章把(下面几篇文章我也正在学习中)... 7.特性(Attribute) 特性这个东西,在面向对象编程中有着非常重要的最用,在架构设计框架的时候,考虑使用特性的几率会非常的大! 特性结合反射技术就可以实现依赖注入,以前看到公司一个项目在写测试代码的时候,总是给每个方法加上[RollBack]的特性,当方法结束后,所有数据库的操作都将会回滚,我很费解,因为RollBack是自己定义的,怎么就一加上这个特性就自动完成回滚了! 下面就是完整的Rollback代码,可是我在使用它的时候遇到一个问题 ,就是它只可以用于单元测试,我尝试着把它用于一般的方法当中,可是一直没有实现回滚功能,我感到很费解,有兴趣的朋友可以帮我看看... View Code
只能在单元测试里面进行调用: [url=][/url]
1 [TestClass()] 2 public class ProgramTest : TestFixture //继承这个类 3 {48 [TestMethod()]49 [RollBack()] //添加这个RollBack特性,就能实现回滚了50 public void MyTestTest()51 {52 SqlConnectionStringBuilder connectionString = new SqlConnectionStringBuilder53 {54 DataSource = @"LBDZ-20120514VC\SQLEXPRESS",55 InitialCatalog = "My",56 };57 connectionString.IntegratedSecurity = true;58 59 using (SqlConnection conn = new SqlConnection(connectionString.ToString()))60 {61 conn.Open();62 SqlCommand cmd = conn.CreateCommand();63 cmd.CommandText = "INSERT INTO dbo.MyTable ( id) VALUES ( 6666 )";64 cmd.ExecuteNonQuery();65 Console.WriteLine("OK");66 } 68 Assert.IsTrue(true);69 }70 }
[url=][/url]
Question:这个RollBack我至今还没有弄懂它怎么来实现的,如果那个园友能看懂的话,可以私信给我或留言给我,我会打心里感谢你的,可能会涉及到AOP和IOC的知识,希望大家帮帮我把,纠结了很长时间啦... 8.委托和事件 其实把理解事件跟字段和属性联系起来,虽然这样说可能会不严谨点,但是从一些大的方面讲事件就是对委托的封装,类似于属性对字段的封装,这种说法还是行得通的! 想要定义一个完整的委托和事件,需要经历一下步骤(需要注意一些命名规范): ① 定义事件 → 委托使用微软提供的EventHadler<TEventArgs>泛型委托,一般都会有两个参数: A) “object sender”定义的事件依附的对象,也就是事件定义在那个类中,那么这个参数就为这个类的实例化对象,一般都会用“This”! B) “EventArgs e”也就是用于传递一些参数信息的对象,也可以使用自己定制的参数了 ② 创建参数类 → 如果有必要定制的数据参数类(这个类似于创建自己的实体类用来传递信息),这个参数类应该继承于EventArgs这个类! ③ 执行事件 → 其实在执行事件的时候还是有一定的规范的,比如方法名必须为“On+事件名”,还有在执行事件要判断下时候为null,,然后在调用! ④ 注册事件 → 调用事件(在传递事件对象的时候最好用“this”关键字) ⑤ 依附事件的方法 → 最后定义依附在这个事件中的方法,也就是执行这个事件的方法体,深入了解,其实依附事件中的方法其实都最终依附在事件衣服的委托中,这个委托会生成一个委托实例,以及一个委托链! 委托和事件定义语法: 委托: 访问修饰符 + delegate + 返回值类型 + 委托名(参数列表); 事件: 访问修饰符 + event + 委托名 + 事件名; 委托和事件跟观察者模式联系比较密切,可是我还是没有理解它的精髓,可能是我还太菜了... 总结:灵活运用事件和委托将会给你的程序带来更好的扩展性,这需要丰富经验的积累,好了推荐几篇我曾经学习过的文章把! 9.线程 对于线程学习过,可是一直没有做过多线程的项目,一直没有领悟到它的精髓,也只能停留在表面的高度! 我就想说下Thread中的后台线程和前台线程(默认为“前台线程”),在这里总结下(其实我也是学习前辈们的知识)。 前台线程:当所有的前台的线程都执行完毕以后才会退出程序! 后台线程:对于后台线程,程序是不管你是否是执行完成的,不过当你程序一旦强制退出,后台线程也会终止的! [url=][/url]
1 Thread thread = new Thread(delegate()2 {3 Console.WriteLine("线程开始工作");4 Thread.Sleep(2000); //暂停两秒钟5 Console.WriteLine("线程结束");6 });7 thread.IsBackground = true; //分别设置为true和false,看看控制台运行的情况,我相信你能很快明白的8 thread.Start();9 Console.WriteLine("主线程结束");
[url=][/url]
总结:设置为后台线程相当于我们说的异步,而前台线程就相当于同步,执行好线程在执行主程序! 能够熟练使用多线程,还是要在项目中不断的实践,可是项目是可遇而不可求的东西,现在我的项目是肯定要不到了,只能自己看看文章,熟悉熟悉知识啊... 10.六种异步方式 10.1 委托异步模型 使用的是委托的BeginInvoke和EndInvoke异步执行模式! 必须要有两个条件: ① 必须要有个委托作为寄宿体 ② 执行函数 ExecuteFunction ③ 回调函数 CallBackFunction ,所谓的回调函数就是获取执行函数的返回值! 有了上面三种条件之后,就可以直接调用Begin和End进行委托异步编程了,其中还有细节问题需要注意,下面我们就一一来看! 具体思路步骤: ① 选择一个适合的委托类型,如参数列表,返回值类型 ② 创建一个执行函数,必须跟委托的参数列表和返回值类型对应起来 ③ 创建一个回调函数,它只有一个参数没有返回值,参数类型为IAsyncResult类型,这是使用委托实现异步的规范写法,不可改变 代码实现: [url=][/url]
1 //写一段简洁的代码 2 private void button1_Click(object sender, EventArgs e) 3 { 4 //定义委托,并指定异步的执行方法 5 Func<string, string> func = new Func<string, string>(ExecuteFunction); 6
14.4 关于注释(书中讲到的注释规则让我很有同感,因为现在项目中就有这样的现象) 请注意:注释也许真的不需要,学会使用代码就能完整表达设计和逻辑意图! 常见现象:代码在变动,在演化,彼此分离和重合,可是注释并不总是随着变动,上图: 所以只有代码才能真正的告诉你它在干什么,它有什么作用! Note:与其花时间编写解释你那糟糕代码的注释,还不如花时间清洁那糟糕的代码! 坏注释和多余注释的几点原则: ①有时候一段坏的注释不紧会影响代码的整洁,而且还会占用一定的时间,最终读注释的时间比阅读代码的时间还长,所以这种注释要删除它,影响我们阅读代码的时间 ②日志式注释:这种注释最有感触,在class开头写上每次修改的记录,这种方式也有好处,但是这种情况应该在没有源代码控制的情况下进行记录(其实我听赞同在class头上写上每次修改的版本的) ③关于废话性和误导性的注释坚决不能存在 ④能用函数和变量是就别用注释,所以变量和函数的命名真的很重要,可以让人一眼就能看出它的作用 请明白非常重要的一点:注释的作用就是解释未能自行解释的代码,如果注释本身还需要解释,就太遗憾了! 14.5 单元测试的重要性 一直觉得单元测试可有可无,那是因为我只是学习过从来没有真正的在项目中运用过,可是最近我下了狠心要在项目中构建一个单元测试框架,终于被我搞定了,我也感悟到单元测试对一个开发人员的重要性,想学习的话可以看看这篇文章:走进单元测试五:单元测试文章系列目录 建议大家看看<<代码整洁之道>>和<<.NET设计规范>>以及<<程序猿修炼之道之单元测试>> 15.其它知识点 15.1 const和readonly本质区别 理解两者是在“编译时”还是“运行时”常量,以及两者的作用域,那么它们将不会这么神秘! 编译时OR运行时: const:编译时 readonly:运行时 作用域: const:①本身就是静态变量 ②只能定义基本类型,如int,string等等 ③局部变量和全局变量都可以定义 ④一旦定义就不能修改 readonly:①不是静态变量,如果需要需加上“static”关键字 ②可以定义一切类型,可以是自己自定义的对象 ③只能定义全局变量 ④一旦定义可以在构造函数里面进行初始化变量 总结:园子里面还有很多对于它们性能方面的文章,有兴趣的可以搜搜看,推荐使用“readonly”吧! 15.2 is和as操作符 16.2.1 As和强制转化最本质的区别 As:进行转换的时候永远不是出现异常,当转换失败时会返回一个“null”值,所以你只需要进行一个null值的判断就知道转换失败还是成功了! 强制转化:会出现转换失败并抛出异常,所以我们都需要使用“try/catch”来捕获转换出错的异常,也可以使用“is”来判断是否是你要转换的类型! 16.2.2 一些常见注意点 ① as不能用于值类型的转化 如:object number=100;int numberOne = number as int; 这是因为如果转换失败,那么就会返回一个“null”值,但是值类型是不允许为“null”的,所以在语法上是行不通的,就是你写成了“int?”也是不行的! ② 使用Is配合强制转换来进行类型转换 首先使用“Is”来判断是否是我需要转换的类型,然后在进行强制转换 View Code
③ 在没有泛型的foreach中,也是把“object”进行强制转化成所需要的类型,代码如下: View Code
Note:或者使用GetType()方法来精确检测是否是你想要的转换类型! 15.3 运算符操作以及类型转化操作重载 这两个知识点还是比较容易学习的,一个是操作符的重载,一个是用于自定义强制转换的方法(你也可以使用“as”进行强制转换),只要稍加注意一些语法就好了!
[url=][/url]
1 public class MyPerson 2 { 3 public string Name { get; set; } 4 5 /* 6 * ①必须为静态 7 * ②关键字operator 8 * ③需要定义重载的操作符 9 * ④定义返回值类型10 */11 public static MyPerson operator +(MyPerson personFather, MyPerson personMother)12 {13 return new MyPerson() { Name = personFather.Name + personMother.Name };14 }15 16 /*17 ①必须为静态18 * ②关键字“explict”和operator”19 * ③需要转化的类型:MyPerson20 */21 public static explicit operator MyPerson(MyPeople myPeople)22 {23 return new MyPerson(){ Name="YCG" };24 }25 }26 public class MyPeople27 { }
[url=][/url]
具体的用法如下: [url=][/url]
1 MyPerson personOne = new MyPerson() { Name = "AAAAAA" };2 MyPerson personTwo = new MyPerson() { Name = "BBBBB" };3 MyPerson personThree = personOne + personTwo; //操作符重载4 Console.WriteLine(personThree.Name); 5 6 MyPeople people = new MyPeople() { Name = "wang wei" };7 MyPerson personFour = (MyPerson)people; //类型强制转换8 Console.WriteLine(personFour.Name);
[url=][/url]
15.4 ToString方法 15.5 数据实体模型(Tuple)以及匿名类型 16.5.1 Tuple实际上就是一个匿名的实体的模型,它的用处在于不要自己定义一个实实在在的Entity,使用它就能达到效果! View Code
Note:在查看源代码的时候注意它的第八个参数: 16.5.2 匿名类型 1 var data = new { number = 11111, str = "ssssss" }; //不需要定义变量的类型,如果想知道匿名类型底层源码怎么写的,可以使用反编译查看源码,一目了然了!2 Console.WriteLine(data.number + data.str);
15.6 String和StringBuilder详解 17.Remoting
|