.Net培训个人总结笔记18

学习交流,欢迎转载。转载请注明文章来源:http://www.cnblogs.com/lgjspace/archive/2011/10/12/2218227.html

 

区别:
抽象类中的抽象方法和标记为 Virtual、Override 的方法的区别:
1.抽象类中的抽象方法必须强制要求在其所有非抽象的子类中一定要有其对应的 override 实现;而被标识为 virtual 的虚方法在其所在类的子类中可以 override,也可以不 override,不会强制要求。
2.抽象类中的抽象方法必须要求不能有任何实现,而被标识为 virtual 的虚方法必须要有自己的实现,就算是“空实现”(即只有一对大括号,没有任何实现代码)也必须得有。

 

细节:
一个类中一旦有抽象成员,则该类必须标记为抽象类。抽象类是不能被“new”的,即不能被创建对象。

 

经验:
在实际开发中,当类中的方法不知道怎么实现时,就要考虑到是否应该标记为抽象方法。
例如 Person 类中的 SayHello 方法,“人类”是不知道要怎么来“SayHello”的,如果此时有 Chinese 类继承了 Person 类,则 Chinese 类就知道怎么“SayHello”了,例如“你好”、“新年好”等,此时 Chinese 类中的 SayHello 方法就应该有实现。
从功能上说,抽象类中的抽象方法都是定好了“实现协议”(方法头),但具体的细节实现要在非抽象的子类中进行,即相当于在抽象类中规定了必须实现的动作,至于该动作如何实现等细则就交给继承自该抽象类的子类决定,单从这方面来说比较类似于接口。

 

细节:
在 VSS 中,如果在受管理的代码中添加一个项目或文件等,签入保存时要整个项目地签入(就算只是添加了一个文件而不是整个项目,也要整个项目再次签入),因为项目里的文件发生变动,会牵扯到其他文件都会有所改变(例如:*.csproj 项目文件等)。

 

细节:
泛型类的声明只需要在类名后面加上尖括号<>以及在尖括号<>中加上一个指代字符即可,一般推荐写为<T>,但这不是硬性规定,可以为以下的任意情况:
class P<a> //可以为小写的字符串代表一个类型,而不规定必须要大写字符来表示。
{
}
class P<类型一> //甚至可以用汉字来代表一个类型,而不规定必须要英文字符来表示,但极不推荐。
{
}
class P<aBc> //可以用多个字符来表示一个类型占用符,而不规定只能用一个字母字符代表一个类型。
{
}
class P<aBc,cC,dd> //可以在尖括号中指定多个类型占用符,而不是只能有一个。
{
}
class P<a1,_b,c_23> //C#的命名规则也适用于类型占用符的命名。
{
}

 

细节:
不仅可以自定义泛型类,还可以自定义泛型方法、泛型接口等。
声明泛型方法的方法:在方法名和方法参数的括号之间加上尖括号<>以及在尖括号<>中加上类型占用符(如:T 等)即可实现最简单的泛型方法的声明。
当然,方法的返回值和方法体里的成员都可以使用该类型占用符,但这些地方只能使用已经在方法名后定好的类型占用符,而不能在这里声明新的泛型类型占用符,在方法中可以声明泛型类型占用符的地方是前面说的方法名和方法参数括号之间的“<T>”,这才是“决定整个泛型方法用什么类型”的地方。

 

细节:
类型占用符(暂命名为T)可以用来表示:方法的参数类型、方法的返回值类型、类中属性类型、类中的字段类型等。

 

细节:
泛型的本质就是:当被调用时才被确定所有被标记了类型占用符的数据类型,在这方面来看,有点类似于运行是常量 readonly,也是程序运行时才确定数据的相应类型。

 

细节:
.Net 中提供了一个泛型的 Cast<T>() 方法,用来为各种实现了非泛型 IEnumerable 接口的对象转换成泛型版本的 IEnumerable<T> 接口。但这种转换要求被转换的对象所装载的元素必须全都是将要被转换的目标类型 T,否则报错“指定的转换无效”,而且不是在转换数据时就报错,而是在使用数据时才报错。类似地,.Net 中提供了一个 OfType<T>() 方法,功能和泛型的 Cast<T>() 方法差不多,区别只是:OfType<T>() 方法只会转换源对象中类型和待转换的目标类型一致的元素,和待转换的目标类型不一致的元素不会被转换。

 

细节:
扩展方法的实现方法:
1. 扩展方法首先要求自己必须是静态方法,自己所在的类必须为静态类。
2. 在方法头的参数列表中,选定一个将要操作的参数,然后在该参数的类型名(注意不是变量名)前面加上关键字 this 即可实现,即你打算要在哪个变量后面“点出”该方法来,对哪个变量进行操作,就在参数列表中代表该变量的参数的参数类型名前加上 this,如:
static class StringHelper
{
    public static string SayHello(this string s)
    {
        return "Hello! " + s;
    }
}
假设执行如下代码:
string s1 = "s1";
s1.StringHelper.SayHello(); //相当于把变量 s1 当作 SayHello 方法中加了关键字 this 的那个参数来传入该方法。
该代码执行结果如下:
"Hello! s1"
3. 在扩展方法中,带关键字 this 的参数一定要放在参数列表的第一个。
4. 实现扩展方法的条件:
   (1) 类必须是 static,这个类要被当前的代码 using 进来;
   (2) 方法也必须是 public static;
   (3) 第一个参数是要扩展的类型,加上 this 关键字,在写扩展方法的时候就像写普通方法一样。

 

注意!!!
1.扩展方法也可以当成普通的静态方法那样调用,此时不是“点”出该方法,而是要像平时调用方法时那样直接把参数传进方法,带 this 关键字的参数也不例外。如下面的例子所示:
(假设下面的 ReadAllLines() 方法为静态类 StringHelper 下的一个扩展方法)
FileStream fs = File.OpenRead("C:\boot.ini");
fs.ReadAllLines(); //执行结果和下面一行的执行结果一样。
StringHelper.ReadAllLines(fs);  //扩展方法既可以像调用普通类的成员方法那样“点”出来,也可以像调用普通静态方法那样使用。
2.扩展方法不能调用被扩展对象内部的私有成员,因为扩展方法的调用方式本质上还是外部调用。

 

细节:
如何区分普通方法和扩展方法:
扩展方法在 VS 中的标记图标不同于普通的方法图标,例如在自动完成提示框中,扩展方法的图标除了带有像普通方法图标的红色小方块之外,右半部分还带有一个向下的蓝色小箭头。同时,在信息提示框(ToolTips)的提示信息开头处还带有“(扩展名)”的提示字样。

 

说法:
扩展方法只是 C# 提供的一个“语法糖”而已,即使用起来比普通的“调用静态方法的方式”简单一点、美观一点而已,除此之外和普通的静态方法没有什么本质上的区别。

 

细节:
扩展方法的优点:
不用记住扩展方法声明在哪个类中,只要在相应的对象或数据后面一“点”就出来了。

 

拓展:
扩展方法例子:
1.Cast<T>() 方法、 OfType<T>() 方法、Max() 方法等。
2.在下面讲到的 Linq 中有很多都是扩展方法,讲扩展方法就是为后面讲 Linq 做准备的。

 

细节:
Linq 的语法看起来非常像 SQL 语句,但是和 SQL 无关。Linq 的目的是:以统一的方式对数据进行操作。

 

细节:
var 关键字的本质:
C# 中的 var 只是用来简化类型的声明,不是弱类型的标志。var 本质上是一种类型推断(在编译时就完成推断,而非运行时才完成推断)。例如:
var i = 2;
程序在编译到这句时由于有关键字“var”,编译器会先对等于号右边的数据类型进行判断,判断出准确的类型之后才决定“var”是什么类型,即相当于“由右边的数据类型来决定左边的 var 的数据类型”。
注意:var 只能用在方法内部的赋值语句中,不能用在类的属性、类的字段、方法返回值、方法的参数列表等场合。

 

注意:
1. var 关键字在某些场合可以为代码的编写提供了方便,但不能全部的类型声明都用 var ,因为这样会模糊了代码中的确切数据类型,大大降低了代码的可读性。
2. var 和扩展方法一样,都是“语法糖”,不吃不好,吃多了也不好。

 

细节:
“匿名类型”会被“复用”的条件:
当新“new”出来的匿名类型的所有成员和前面已声明的匿名类的成员的类型和个数都相吻合时,这个新的匿名类型会复用前面已声明的且吻合自己的匿名类型。

 

细节:
在“匿名类型”中,如果被推断的匿名类型中的属性命名冲突,则必须显式指定。
如: var p = new { name.Length, Length2 = "C#".Length };

 

细节:
在 Linq 中,where 语句、orderby 语句、和 group by 语句等都要放在 select 语句之前。

 

重点:
Linq 处理的是 IEnumerable 序列。

 

细节:
Linq 体现的是“函数式编程”的思想,即代码中没有 if、else、while、for 等较为低级的语法的东西,把主要精力都集中在业务需求的逻辑上。在这方面和后面将要学到的 JQuery 有点类似。

 

细节:
Linq 的降序排列(默认是升序排列):

1 int[] values = { 1, 25, 250, 88, 99};
2 var result = from value in values
3 orderby value descending //加上了关键字 descending 代表要求“将序排列”,默认是升序排列。
4 select value;

本段代码中“orderby value descending”的意思是:按 value 来进行排序,排序是降序排序。

 

细节:
字符和字符串都可以“排序”,因为字符和字符串都有顺序,顺序是按开头字母的字母顺序来排列。

 

细节:
Linq 的分组:

1 int[] values = { 1, 2, 5, 2, 3, 5, 5, 3, 4, 3, 3 };
2 var result = from i in values
3 group i by i into g //按照 i 进行分组,分组后的数据用 g 来装载和表示
4 select new { 数字 = g.Key, 个数 = g.Count() }; //g.Key 表示“分组的依据项”,在这里即为被用来分组的数组中的数字;g.Count() 返回的是“每组的个数”,例如在这代码中,“5”的那一组的个数为 3 个。

 

细节:
前面的分组案例中出现的“group i by i”中两个“i”所指代的意义是有区别的:
group 后的 i 指的是“要拿来分组的对象或元素”,而 by 后的 i 指的是“分组是依据什么来分组”。

 

细节:
匿名类型里面可以放方法,但要写成“方法名 = 委托变量”的形式,即在外部先定义好一个方法,把方法赋给委托,然后再把委托赋给匿名类型里面的成员。

 

细节:
1.Linq 中 Except() 方法的顺序问题:
假如有下面代码:

1 int[] values1 = { 1, 3, 5, 6, 7, 8, 9, 10 };
2 int[] values2 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
3 var result = values2.Except(values1);

当执行到代码“values2.Except(values1)”时,该代码的功能是:返回集合 values2 中除去了集合 values1 中所有元素之外的剩余元素组成的集合,例如这代码段执行的结果为整数数组:{ 2, 4 }。
2.Linq 中 Single() 方法的使用:
该方法能保证集合中有且只有一个元素,如果集合中有一个以上或者没有任何元素都会报错,如下面代码所示:

1 int[] v1 = { 1, 2 };
2 int i = v1.Single(); //执行到这句时会报错“序列包含一个以上的元素”。

 

经验:
在小数据量、对性能要求不高的场合下用 Linq 来处理很方便,而且延迟加载机制降低了内存占用,比一般人写的程序效率都要高。

posted @ 2011-10-12 23:47  梁国锦  阅读(193)  评论(0编辑  收藏  举报