物联网网站开发公司,如何用h5做网站,温州网站建设技术外包,做一个网站的市场价在此重申一下#xff0c;本文仅代表个人观点#xff0c;如有不妥之处#xff0c;还请自己辨别。 第一代的值类型装箱与拆箱的效率极其低下#xff0c;特别是在集合中的表现#xff0c;所以第二代C#重点解决了装箱的问题#xff0c;加入了泛型。1. 泛型 - 珍惜生命#x… 在此重申一下本文仅代表个人观点如有不妥之处还请自己辨别。 第一代的值类型装箱与拆箱的效率极其低下特别是在集合中的表现所以第二代C#重点解决了装箱的问题加入了泛型。1. 泛型 - 珍惜生命远离装箱 集合作为通用的容器为了兼容各种类型不得已使用根类Object作为成员类型这在C#1.0中带来了很大的装箱拆箱问题。为了C#光明的前途这个问题必须要解决而且要解决好。 C模板是一个有用的启迪虽然C模板的运行机制不一样但是思路确实是正确的。 带有形参的类型也就是C#中的泛型作为一种方案解决了装箱拆箱类型安全重用集合的功能防止具有相似功能的类泛滥等问题。泛型最大的战场就是在集合中以ListTQueueTStackT等泛型版本的集合基本取代了第一代中非泛型版本集合的使用场合。当然除了在集合中泛型在其他的地方也有广泛的用途因为程序员都是懒的重用和应对变化是计算机编程技术向前发展最根本的动力。 为了达到类型安全(比如调用的方法要存在)就必须有约定。本着早发现早解决的思路在编译阶段能发现的问题最好还是在编译阶段就发现所以泛型就有了约束条件。泛型的约束常见的是下面几种
a. 构造函数约束(使用new关键字)这个约束要求实例化泛型参数的时候要求传入的类必须有公开的无参构造函数。
b. 值类型约束(使用struct关键字)这个约束要求实例化泛型参数的类型必须是值类型。
c. 引用类型约束(使用class关键字)这个约束要求实例化泛型参数的类型必须是引用类型。
d. 继承关系约束(使用具体制定的类或接口)这个约束要求实例化泛型参数的类型必须是指定类型或是其子类。 当然了泛型参数的约束是可以同时存在多个的参看下面的例子 public class Employee
{}
class MyListT, V where T: Employee, IComparableTwhere V: new()
{
} 如果不指定约束条件那么默认的约束条件是Object这个就不多讲了。 当使用泛型方法的时候需要注意在同一个对象中泛型版本与非泛型版本的方法如果编译时能明确关联到不同的定义是构成重载的。例如 public void Function1T(T a);
public void Function1U(U a);
这样是不能构成泛型方法的重载。因为编译器无法确定泛型类型T和U是否不同也就无法确定这两个方法是否不同public void Function1T(int x);
public void Function1(int x);
这样可以构成重载public void Function1T(T t) where T:A;
public void Function1T(T t) where T:B;
这样不能构成泛型方法的重载。因为编译器无法确定约束条件中的A和B是否不同也就无法确定这两个方法是否不同 使用泛型就简单了直接把类型塞给形参然后当普通的类型使用就可以了。例如
Listint ages new Listint();
ages.Add(0);
ages.Add(1);
ages.Remove(1);
2. 匿名函数delegate 在C# 2.0中终于实例化一个delegate不再需要使用通用的new方式了。使用delegate关键字就可以直接去实例化一个delegate。这种没有名字的函数就是匿名函数。这个不知道是不是语法糖的玩意儿使用起来确实比先定义一个函数然后new实例的方式要方便。不过最方便的使用方式将在下一版中将会到来。 delegate void TestDelegate(string s);
static void M(string s)
{Console.WriteLine(s);
}//C# 1.0的方式
TestDelegate testDelA new TestDelegate(M);//C# 2.0 匿名方法
TestDelegate testDelB delegate(string s) { Console.WriteLine(s); }; 谈到匿名函数不得不说说闭包的概念。 如果把函数的工作范围比作一个监狱的话函数内定义的变量就都是监狱中的囚犯它们只能在这个范围内工作。一旦方法调用结束了CLR就要回收线程堆栈空间恢复函数调用前的现场这些在函数中定义的变量就全部被销毁或者待销毁。但是有一种情况是不一样的那就是某个变量的工作范围被人为的延长了通俗的讲就像是某囚犯越狱了它的工作范围超过了划定的监狱范围这个时候它的生命周期就延长了。 闭包就是使用函数作为手段延长外层函数中定义的变量的作用域和生命周期的现象作为手段的这个函数就是闭包函数。看一个例子 class Program{static void Main(string[] args){ListAction actions getActions();foreach (var item in actions){item.Invoke();}}static ListAction getActions(){ListAction actions new ListAction();for (int i 0; i 5; i){Action item delegate() { Console.WriteLine(i); };actions.Add(item);}return actions;}} 你可以试试运行这个例子结果和你预想的一致吗这个例子会输出5个5而不是0到4出现这个现象的原因就是闭包。getActions函数中的变量i被匿名函数引用了它在getActions调用结束后还会一直存活到匿名函数执行结束。但是匿名函数是后面才调用的执行它们的时候i早就循环完毕值是5所以最终所有的匿名函数执行结果都是输出5这是由闭包现象导致的一个bug。 要想修复这个由闭包导致的问题方法基本上是破坏闭包引用方式多种多样下面是简单的利用值类型的深拷贝实现目的。
第一个方法让闭包引用不再指向同一个变量 for (int i 0; i 5; i)
{int j i;Action item delegate() { Console.WriteLine(j); };actions.Add(item);
} 第二个方法包上一层函数来构造新的作用域 static ListAction getActions()
{ListAction actions new ListAction();for (int i 0; i 5; i){Action item ActionMethod(i);actions.Add(item);}return actions;
}static Action ActionMethod(int p)
{return delegate(){Console.WriteLine(p);};
} 闭包现象提醒我们使用匿名函数和3.0中的Lambda表达式时都要时刻注意变量的来源。
3. 迭代器 在C# 1.0中集合实现迭代器模式是需要实现IEnumerable的这个大家还记得吧这个接口的核心就是GetEnumerator方法。实现这个接口主要是为了得到Enumerator对象然后通过其提供的方法遍历集合(主要是Current属性和MoveNext方法)。自己去实现这些还是比较麻烦的先需要定义一个Enumerator对象然后在自定义的集合对象中还需要实现IEnumerable接口返回定义的Enumerator对象于是一个新的语法糖就出现了: yield关键字。 在C# 2.0中只需要在自定义的集合对象中还需要实现IEnumerable接口返回一个Enumerator对象就行了这个创建Enumerator对象的工作就由编译器自己完成了。看一个简单的小例子 public class StackT:IEnumerableT
{T[] items;int count;public void Push(T data){...}public T Pop(){...}public IEnumeratorT GetEnumerator(){for(int icount-1;i0;--i){yield return items[i];}}
} 使用yield return创建一个Enumerator对象是不是很方便编译器遇到yield return会创建一个Enumerator对象并自动维护这个对象。 当然了多数时候foreach必要遍历集合中的每一个元素这个时候使用yield return配合for循环枚举每个元素就可以了但是有时候只需要返回满足条件的部分元素这个时候就要结合yield break中断枚举了看一下 //使用yield break中断迭代
for(int icount-1;i0;--i)
{yield return items[i];if(items[i]10){yield break;}
} 4. 可空类型 这个特性我觉得又是把值类型设计成引用类型Object类子类后微软生产的怪语法。空值是引用类型的默认值0值是值类型的默认值。那么在某些场合比如从数据库中的记录取到内存中以后没有值代表的是空值但是字段的类型却是值类型怎么搞呢于是整出了可空类型。当然了这个问题可以通过在设计表的时候给字段设计一个默认值来解决但是有的时候某些字段的设置默认值是没有意义的比如年龄0有意义吗 可空类型的概念很简单没什么可说的不过一个相似的语法却让我感到很舒服那就是??操作符。这是一个二元操作符如果第一个操作数是空值则执行第二个操作数代表的操作并返回其结果。例如 static void Main(string[] args)
{int? age null;age age ?? 10;Console.WriteLine(age);
} 5. 部分类与部分方法 这一特性还是比较好用的终于不用把所有的内容挤到一起了终于可以申明和实现相分离了虽然好像以前也可以做到但是现在这项权利也下放给人民群众了。partial关键字带来了这一切也带来了一定的扩展性。这个特性也比较简单就是使用partial关键字。编译的时候这些文件中定义的部分类会被合并。部分方法是3.0的特性不过没什么新意就放到2.0一起说吧。 使用这个特性的时候需要注意
a. 部分方法只能在部分类中定义。
b. 部分类和部分方法的签名应该是一致的。
c. partial用在类型上的时候只能出现在紧靠关键字 class、struct 或 interface 前面的位置。
d. public等访问修饰符必须出现在partial前面。
f. partial定义的东西必须是在同一程序集和模块中。 看一个简单的例子 // File1.cs
namespace PC
{partial class A{int num 0;void MethodA() { }partial void MethodC();}
}// File2.cs
namespace PC
{partial class A{void MethodB() { }partial void MethodC() { }}
} 这里需要注意一下MethodC的申明和定义是分开的就可以了。 还有一点很多人认为partial破坏了类的封装性实际上谈不上。因为一个类能分部就说明类的设计者认为是需要保留这个扩展性的所以后面的人才可以给这个类添加一些新的东西。
6. 静态类 这个特性也是比较符合实际情况的很多情况下某些对象只需要实例化一次然后到处使用单件模式是可以实现这个目的现在静态类也是一个新的选择。 静态类只能含有静态成员所以构造函数也是静态的既然是静态的那么它与继承就没什么关系了。静态类从首次调用的时候创建一直到程序结束时销毁。 简单看一个小例子 public static class A
{static string message Message;static A(){Console.WriteLine(Initialize!);}public static void M(){Console.WriteLine(message);}
} 不过据经验讲有没有静态构造函数对静态类的构造时间是有影响的一个是出现在首次使用对象成员的时候一个是程序集加载的时候不过分清这个实在没什么意义有兴趣的同学自己研究吧。 C#2.0的新特性绝不止这几个但是对程序猿们影响比较大的都在这了更多的就参看微软的MSDN吧。