.Net培训个人总结笔记7
学习交流,欢迎转载。转载请注明文章来源:http://www.cnblogs.com/lgjspace/archive/2011/10/12/2212931.html
技巧:
如果输入了一部分代码之后才想到要在该代码段外加上if、for、while等在外面包裹着
的代码时,可以选中需要被包在里头的代码,然后按“Ctrl + K + S”,然后输入“if
”即可。
细节:
一般的应用程序都有一个入口函数(类似于 C# 程序中的 Main 方法),而入口函数一
般都带有启动参数(即传入入口函数的参数),一般是初始化程序或者在打开时设定某
些初始设置值等,例如:IE也有入口函数参数,往该参数输入一个网址,即可实现“打
开IE立刻转到该设定的网址的页面”的效果,类似于默认主页的效果。这参数在用
Process 类的对象打开进程时同样可以为进程设定参数,代码很简单,如下所示(两种
都可以):
1.只传入进程名,没有参数:
Process pro = Process.Start(name);
2.同时传入进程名和参数:
Process pro = Process.Start(name, arg);
//Process pro = Process.Start(new ProcessStartInfo(name, arg)); //也可以使
用这个重构方法。
经验:
在C#程序中,真正能实现“实时机制”的只能靠死循环来做到。
细节:
单纯的方法名(不带方法名后的参数列表及方法体)实际上是该方法在内存当中的入口地址。
细节:
指针的本质就是一个整型数。
细节:
给委托对象“绑定”方法的方式有两种,如下面代码所示(注释为两种方式的描述):
private int Max(int i,int j)
{
return (i - j >= 0 ? i : j);
}
private int Min(int i,int j)
{
return (i - j <= 0 ? i : j);
}
delegate int MyDelegate(int a,int b);
private void BindingFunc()
{
MyDelegate md;
md = this.Max; //方式一:直接把需要绑定的方法名(没有方法名后的括号)付给委托变量名,这是为了能够适应 C/C++ 程序的写法习惯。
md = new MyDelegate(this.Max); //方式二(推荐):通过关键字 new 来创建一个MyDelegate 类的对象并予以初始化(为该委托对象设置指向一个函数),然后再赋值给委托变量。
}
经验:
委托的作用,就是“隔离变化”,只用一个委托,即可指向不同的函数,甚至可以指向多个函数。
重点:
由委托(“取最值”案例)和接口“工厂模式”(IFruit案例)的区别得出的总结:
委托和接口在“隔离变化”方面上都有相似之处,都是可以让程序“抓住”不变的,来控制“变化”的,以实现“以不变应万变”。但两者也有各自区别,委托是通过绑定不同的方法来实现这种“隔离变化”的效果,而接口的“工厂模式”则是通过类的多态性来绑定不同的类,从而执行各自不同的方法,从这点来看,委托相当于“方法的多态化”,而接口相当于“类的多态化”,从性能上来说,委托由于直接“绑定”的是方法,调用起来会更快更节省性能。
经验:
面试重点问题:
1.委托(自定义事件)是怎么回事
2.接口、抽象类之间的区别
细节:
CheckForIllegalCrossThreadCalls,从字面意思来看,即“检查非法的跨线程调用”,设为 true 即为检查是否进行了非法的跨线程调用,而设为 false 则为不检查,但这样极不推荐。
细节:
要想在同一时间做两件以上的事情时,就要用到多线程。
注意:
当线程用完时必须要显式调用“线程对象.Abort()”方法,这样可以让 CLR 立刻进行垃圾回收。而且程序执行Abort()方法时就算是正常情况下也会抛出异常,所以对该方法应该还要“外包”一个 try catch 语句。
细节:
线程对象的Start()、Abort()等方法不是说一调用方法就立刻执行操作的,因为线程是操作系统决定的,当系统繁忙的时候可能会有延时。
说法:
委托就是“安全的函数指针”。
细节:
当调用线程的 Abort() 方法时,正常情况下该方法也会抛出异常,这是正常现象。
重点:
在“文件拷贝”的案例中,之所以窗口在拷贝的时候会“死掉”,是因为“循环”的计算操作和界面的显示操作同时共用了一个主线程,由于“计算”操作的优先级高于“显示”操作的优先级,所以循环时看不到界面显示的变化,甚至移动不了窗体,形成“假死”的现象。要解决该问题可以通过使用多线程来完成。创建一个新的线程,让该“循环”操作由线程初始化时绑定的方法内部来执行,这样即可实现“计算”和“显示”的分工,解决了界面假死问题。
细节:
当循环频率不需要那么高时,可以在循环体中加上“Thread.Sleep(毫秒的秒数)”,这样可以降低CPU性能的消耗(降低消耗程度随参数中的毫秒值的递增而提高)。
原理:
线程池(ThreadPool):
不用手动控制线程,线程池相当于充当一个“线程资源库”,需要调用线程的时候就找线程池,线程池会安排线程来完成你交代的任务(方法),这种方法不能够调用线程的Sleep()、Abort()、Suspend()、Resume()、Start()等方法。
线程池的使用方法(例子):
WatchCallback wcb = new WatchCallback(this.Function); //假设Function是函数名。
ThreadPool.QueueUserWorkItem(wcb);
这样即可把需要创建线程来执行的函数 Fucntion 交给线程池来安排线程完成了。
经验:
程序中创建的线程数量最好不要超过(CPU的数量 + 2),但使用线程池时除外。
经验:
凡是对表状结构、链状结构进行操作的时候,同一时间的操作只能有一个,“我访问了你就不能访问”。
细节:
在.Net中可以通过以下几种方式实现线程的同步:
1.Lock语句(其本质上也是通过 Monitors 类来实现的)。
2.MethodlmplAttribute 特性。
3.SynchronizationAttribute特性
4.Monitors 类
5.ReaderWriterLock 类
6.Mutex 类
其中lock语句的使用例子:把不能被两个线程同时操作的方法或不能被并行访问、修改的代码用look语句来包起来即可。
细节:
“(转换类型)”和“as”的两种类型转换方式的区别在于,在转换失败时,前者会报错,后者不会,只会返回 null 值。
经验:
泛型的目的是为了最大程度重用代码,隔离了变化(类型的变化),解决了“相似的操作流程或函数功能”和“不同的数据类型”之间的矛盾所造成的代码重复(例如“比较两个整数的大小”和“比较两个double类型值的大小”,两个功能相同,但由于数据类型不一样,必须为两个需求分别实现各自的方法,从而造成代码冗余)。
细节:
泛型中的 <T> 里面的“T”指的是一种“已知”的类型,“已知”指的是运行之前的“已知”,而不是编译之前的的“已知”。但是在编写代码的时候是未知的,因此在代码中不能出现 “<T> a = 12;”或“<T> b = null”等这样的要以“此时已经知道 T 的类型”为前提的操作(第一句必须要求 T 为值类型,而第二句必须要求 T 为引用类型,因为“null”不能赋给值类型,“12”不能赋给引用类型和非数值类型)。
经验:
泛型编程一般多用于交换数据等这种同种数据之间的模糊操作,不能用于过于具体的数据操作,例如赋值,改值等之类的。
细节:
泛型中的尖括号<>中的类型个数可以是一个或任意多个,例如<T,U,V,W>等,而且里面的T,U,V,W所代表的类型还可以是相同的,但一般不会这么做。
细节:
在泛型中,为泛型变量初始化可以用“T a = default(T)”的形式来实现。
细节:
泛型的限制:
如下面代码所示:
1 public class MyClass<T>
2 {
3 public string GetStringValue(T t)
4 {
5 return t.ToString();
6 }
7 public string GetStringValue(int t)
8 {
9 return t.ToString();
10 }
11 }
12 class Program
13 {
14 public static void Main()
15 {
16 MyClass <int> mc = new MyClass<int>(); //C#规定,凡是有符合泛型类型的重载方法的话,则优先使用非泛型类型的方法,例如本例中则会调用非泛型的方法,而不会执行泛型的方法,这是C#的一个规定。
17 int i = 10;
18 string s = mc.GetStringValue(i);
19 }
20 }