基础:
C# 2.0允许使用default运算符对任意数据类型的默认值进行动态编码。
可以使用default(int)来指定一个int的默认值,用default(string)来指定一个string的默认值(default(string)会返回null,所有引用类型都会返回 null)。
对于T,你可以使用default(T)来初始化
1.泛型
class Class1
{
//泛型的使用语法
Stack<int> point = new Stack<int>();
}
//以下是泛型的定义语法
/// <summary>
/// 堆栈
/// </summary>
/// <typeparam name="T"></typeparam>
public class Stack<T>
{
private T[] _Items;
//存入
public void Push(T data)
{
}
//取出
public T Pop()
{
}
}
泛型可用于类、接口和struct
泛型的特点:你只需要将精力放在算法和模式的创建上,泛型就能保证你的核心代码能由N个不同的数据类型所重用。
泛型的优点:
1.泛型提供了一个强类型的编程模型。
2.编译时类型检查减少了在运行时发生InvalidCastException异常的几率。
3.使用值类型时不再需要装箱操作。减少了内存消耗。
核心:泛型允许你编写程序模式实现,并在以后出现这种模的时候重用实现。
模式描述了在代码中反复出现的问题,而模板为这些反复出现的模式提供了统一的实现。
类型参数—命名的指导原则:
类型参数的命名应该尽量具有描述性。除此之外,为了强调它是一个类型参数,名称应包含一个T前缀。
例如:在定义一个诸如EntityCollection<TEntity>的类时,使用的类型参数名称是“TEntity”。
当适用于任何类型,无附加值时,可以直接使用T。
约束描述性类型名称,例如:假定一个类型参数必须实现IComponent,则类型名称可以是“TComponent”。
对泛型接口的支持对于集合类来说尤其重要,使用泛型最多的地方就是集合类。
高级主题:在一个类中多次实现相同的接口
模板接口造成的另一个结果是,可以使用不同的类型参数来多次实现同一个接口。
1.1约束
泛型允许为类型参数定义约束。
约束声明了泛型要求的类型参数的特征。为了声明一个约束,需要使用where关键字,后跟一对“参数:要求”。其中,“参数”必须是泛型类型中定义的一个参数,而“要求”用于限制类型从中派生的类或接口,或者限制必须存在一个默认构造器,或者限制使用一个引用/值类型约束。
1.1.1接口约束
//类型参数T必须实现IComarable接口。这样做的好处是,在声明类时,可以在不知类型参数的真实类型时,直接使用IComparable的方法和属性。
//此方式又称之为显示接口成员。即接口约束
public class BinaryTree<T> where T:System.IComparable
{
}
1.1.2 基类约束
当需要将一个类型参数限制为特定类的派生类时,使用基类约束实现。
//将类型参数TValue限制为EntityBase的派生类
public class EntityDictionary<TKey,TValue>
:System.Collections.Generic.Dictionary<TKey,TValue>//被继承的类
where TValue:EntityBase
{
}
同时指定了多个约束,那么基类约束必须第一个出现。
和接口约束不同,基类约束只允许指定一个父类。因为C#不允许多继承。而接口约束可以指定多个接口。
1.1.3 struct/class约束
//类型参数限制为一个值类型或者一个引用类型。
public struct Nullable<T>:
IFormattable,IComparable,IComparable<Nullable<T>>,INullable
where T:struct //只需指定关键字struct或者class
{
}
1.1.4多个约束
public class EntityDictionary<TKey,TValue>
:Dictionary<TKey,TValue>
where TKey:IComparable,IFormattable//可以有多个接口。
where TValue:EntityBase//基类约束只能指定一个基类。
{
}
二、委托和Lambda表达式
类之间的关系中,存在着一些常见的模式:将方法作为数据类型使用。发布-订阅模式。
class Program
{
//声明委托数据类型
public delegate bool ComparisonHandler(int first, int second);
//定义一个同委托签名相同的方法,以便把此方法绑定到委托变量上。
public static bool GreaterThan(int first, int second)
{
return first < second;
}
static void Main(string[] args)
{
int first =Convert.ToInt32( Console.In.ReadLine());
int second =Convert.ToInt32( Console.In.ReadLine());
//给委托赋值,即实例化
ComparisonHandler comparisonHandler = GreaterThan;
//调用委托
Console.WriteLine(string.Format("{0}<{1}={2}", first, second, comparisonHandler(first, second)));
Console.ReadKey();
int[] items={1,2,3};
//委托作为参数进行传传递
BubbleSort(items, GreaterThan);
}
public static void BubbleSort(int[] items, ComparisonHandler comparisonHandler)
{
//...
}
匿名方法
所谓匿名方法,就是没有实际方法声明的委托实例。
我们可以利用匿名方法这一新特性来声明一个没有名字的方法,该方法将自动被转换成一个委托。
class Program
{
//声明委托数据类型
public delegate bool ComparisonHandler(int first, int second);
static void Main(string[] args)
{
//匿名方法
ComparisonHandler comparisonMethod=delegate(int first,int second)
{
return first<second;
}
BubbleSort(items,comparisonMethod);
//无参数的匿名方法
delegate{return Console.ReadLine()!=""};
}
}
Lambda表达式
Lambda表达式是比匿名方法更加简洁的一种匿名函数语法。
这种语法不包含delegate关键字,但添加了Lambda运算符=>
//...
//使用语句Lambda来传递委托
BubbleSort(items,(first,second)=>{ return first<second;}
//...
通常,C#要求用一对圆括号来封闭Lambda表达式的参数列表,不管是否指定了这些参数的数据类型。即使是无参数的语句Lambda(代表无输入参数的委托),也要输入一对空白的圆括号。
//无参数的语句Lambda
Func<string> getUserImput=()=>{string input;
do{
input=Console.ReadLine();
}
while(input.Trim().Length==0);
return input;
}
圆括号规则的一个例外是,当编译器能推断出数据类型,而且只有一个输入参数的时候。语句Lambda可以不带贺括号。
//编译器能推断出(process)数据类型,而且只有一个输入参数时,不需要圆括号
IEnumerable<Process> processes = Process.GetProcesses().Where(process => { return process.WorkingSet64 > 10000000; });