总结一些在学习《C#入门经典》一书中的技巧细节以及基础知识点,由于本人是一个objective-c程序员,所以会偶尔碰到C#与objectiv-c比较类似的概念会提醒一下,下面是提高篇,讲述C#泛型技术、委托、事件以及扩展方法。
22.泛型,就是C++中的模板的概念。泛型类是以实例化过程中提供的类型或类为基础建立的。
泛型使用System.Nullable<T>类型提供了使值类型为空的一种方式。如:
System.Nullable<int> nullableInt = null;
可空类型可以用以下语法:
int? nullableInt; //int?就是System.Nullable<int>的缩写。
??运算符称为空接合运算符,是一个二元运算符,允许给可能等于null的表达式提供另一个值。如果第一个操作数不是null,该运算符就等于第一个操作数。否则,该运算符就等于第二个操作数。
op1 ?? op2 // 等价于op1 == null ? op2 : op1
在上面两个表达式中,op1可以是任意可空表达式
23.List<T>和Dictionary<K, V>
下面介绍 System.Collection.Generics名称空间中的两个泛型类型,
a.List<T>泛型集合类型更加快捷,更易于使用。
创建T类型对象的集合如下:
List<T> list = new List<T> ();
List<T>支持的方法和属性如下表:(NT,List<T>还支持Item属性,允许进行类似于数组的访问)
成员 |
说明 |
int Count |
该属性给出集合中项的个数 |
void Add(T item) |
把一个项添加到集合中 |
void AddRange(IEnumerable<T>) |
把多个项添加到集合中 |
IList<T> AsReadOnly() |
给集合返回一个只读接口 |
int Capacity |
获取或设置集合可以包含的项数 |
void Clear() |
删除集合中所有项 |
bool Contains(T item) |
确定item是否包含在集合中 |
void CopyTo(T[] array, int index) |
把集合中的项复制到数组array中,从数组的索引index开始 |
IEnumerator<T> GetEnumerator() |
获取一个IEnumerator<T>实例,用于迭代集合。 |
int indexOf(T item) |
获取item的索引,如果集合中并未包含该项,就返回-1 |
void Insert(int index, T item) |
把item插入到指定的索引位置上 |
bool Remove(T item) |
从集合中删除一个item,并返回true; 如果item不包含在集合中,就返回false。 |
void RemoveAt(int index) |
从集合中删除索引index处的项 |
List<T>支持的排序泛型方法如下表:
泛型方法 |
int IComparable<T>.CompareTo(T otherObj) |
bool IComparable<T>.Equals(T otherObj) |
int IComparer<T>.Compare(T objectA,T objectB) |
bool IComparer<T>.Equals(T objectA, T objectB) |
int IComparer<T>.GetHashCode(T objectA) |
要对List<T>排序,可以在要排序的类型上提供IComparable<T>接口,或者提供IComparer<T>接口.另外还可以提供泛型委托作为排序方法。一般情况下,给列表排序需要一个方法比较两个T类型的对象。在列表中搜索也需要一个方法来检查T类型的对象。这里给出两个泛型委托:
Comparison<T>:这个委托类型用于排序方法,其返回类型和参数如: int method(T objA, T objB)
Predicate<T>:这个委托类型用于搜索方法,其返回类型和参数如:bool method (T targetObj)
pubilc static int class ListDelegate {
public static int Compare( T a, T b) {
if (a >b)
return 1;
else if (a < b)
return -1;
return 0;
}
public static bool SearchSome (T target) {
if (target is MyClass)
return true;
else
return false;
}
}
void Main() {
List<T> pList = new List<T> ();
pList.Add(new T(5));
pList.Add(new T(6));
pList.Add(new T(9));
Comparison<T> sorter = new Comparison<T> (ListDelegate.Compare);
pList.Sort(sorter); //排序
Predicate<T> searcher = new Predicate<T> (ListDelegate.SearchSome);
pList.FindAll(searcher); //搜索到符合条件的对象,返回值为一个List<T>实例;
}
b.Dictionary <K , V>这个类需要实例化两个类型,分别用于键和值。使用例子:
Dictionary<string, int> pDic = new Dictionary<string, int>;
pDic.Add("Yellow", 8);
foreach(string key in pDic.keys) {
Debug.Log(key);
}
foreach ( int value in pDic.values) {
Debug.Log(value);
}
foreach (KeyValuePair<string, int> pair in pDic) {
Debug( pair.Key + " = " + pair.Value);
}
Dictionary<string , int> dic2 = new Dictionary<string, int>(StringComparer.CurrentCultureIgnoreCase); //如果要把自己的类用作键,
// 而且它们不支持Icomparable或IComparable<K>接口,或者使用非默认的过程比较对象,就必须把IComparer<K>接口传递给其构造函数。此处表示使用不区分大小写的方法来比较字符串
24.定义泛型类:
要创建泛型类,只需要在类定义中包含尖括号语法,可以包含任意多个类型:
class MyClass <T1, T2, T3> {
// ....
}
注意,不能假定类提供了什么类型。以下代码不能编译
class MyClass <T1> {
private T1 t1Obj;
public MyClass () {
t1Obj = new T1();
}
}
我们不知道T1是什么,也就不能使用它的构造函数,它甚至可能没有构造函数,或没有可以公共访问的默认构造函数。如果不是用反射(这是用于在运行期间检查类型)等高级技术,就只能假定T1是继承自System.Object的类型或可以封箱到System.Object中的类型。
25.Default关键字,用于给泛型类实例赋予默认值
class MyClass <T1> {
private T1 t1Obj = Default(T1);
//如果t1Obj是引用类型,就赋予它null值;如果它是值类型,就赋予它默认值。数字类型,默认值是0.结构则根据其各个成员的类型以相同的方式初始化为0或null。
}
26.约束类型,可以限制可用于实例化泛型类的类型。约束符必须出现在继承说明符后。
class MyClass <T1, T2> : MyBaseClass, IMyInterface where T1 : Animal where T2 : Cow {
//...
}
//可以通过把一个类型参数用作另一个类型参数的约束
class MyClass<T1,T2> where T2: T1 {
...
}
27.从泛型类中继承
如果某个类型所继承的基类型中受到了约束,该类型不能“解除约束”。也就是说类型T在所继承的基类型使用时,该类型必须受到至少与基类型相同的约束。
class Farm<T> where T : Cow {
...
}
class SuperFarm<T> : Farm <T> where T : SuperCow {
...
}
但是以下代码不能编译
class SuperFarm<T> : Farm <T> where T : struct {
..
}
28,泛型结构
public struct MyStruct <T1, T2> {
public T1 item1;
publci T2 item2;
}
29.定义泛型接口,这与定义泛型类所用的技术相同。其继承规则也与类相同,保持基接口泛型类型参数的约束。
interface MyFarmingInterface <T> where T : Animal {
bool AttemptToBreed (T animal1, T animal2);
}
30.定义泛型方法,泛型方法的参数可以采用与类相同的方式使用约束
public class Defaulter<T1> {
public T2 GetDefault<T2> () where T2 : T1 {
return default(T2);
}
}
31.定义泛型委托。
public delegate T1 MyDelegate<T1, T2> (T2 op1, T2 op2) where T1 : T2;
32,变体是一个类似于多态性的一个概念,但应用于类型参数。它允许使用一个泛型类型代替另一个泛型类型,这些泛型类型仅在所使用的泛型类参数上有区别。
协变允许在两种类型间转换,其中目标类型有一个类型参数,它是源类型的类型参数的基类。协变类型参数用out参数定义,对于接口定义,只能用作返回类型和属性get访问器的类型。
public interface IMethod <out T> {
...
}
static void ListAnimals(IEnumerable<Animal> animals) {
//...
}
static void main() {
List<Cow> cows = new List<Cow>();
cows.Add(new Cow("黄牛"));
ListAnimals(cows); //此处cows变量支持IEnumerable<Cow>接口,通过协变,这个变量传给需要IEnumerable<Animal>类型的参数的方法
}
抗变允许进行与协变相反的转换。抗变类型参数用in参数定义,对于接口定义只能用作方法的参数。
public interface IMethod <in T> {
...
}
public class AnimalNameLengthComparer: IComparer<Animal> {
public Compare(Animal a, Animal b) {
return a.Name.Length.CompareTo(b.name.Length);
}
}
List<Cow> cows = new List<Cow> ();
cows.Add(new Cow("黄牛"));
cows.Sort(new AnimalNameLengthComparer()); //这比较器通过抗变,将IComparer<Cow>传给IComparer<Animal>参数
33.事件,单个事件可供多个处理程序订阅,在该事件发生时,这些处理程序都会被调用,其中包括引发该事件的对象所在的类中的事件处理程序,但事件处理程序也可能在其他类中。要处理事件需要提供一个事件处理方法来订阅事件,该方法的返回类型和参数应该匹配事件指定的委托。
static void WriteChar (object source, ElapsedEventArgs e) {
....
}
Timer timer = new Timer(100);
timer.Elapsed += new ElapsedEventHandler(WritChar); //可简化为:timer.Elapsed += WriteChar;
timer.Start();
定义事件,在定义事件前,必须先定义一个委托类型,以用于该事件,这个委托类型指定了事件处理方法必须拥有的返回类型和参数。定义了委托后,声明事件使用event关键字,并指定要使用的委托类型。
public class MyClass {
public delegate void MessageHandler ( MyClass myClass, string message);
public event MessageHandler MessageArrived;
private Timer timer;
public MyClass () {
timer = new Timer(100);
timer.Elapsed += CheckForMessage;
}
pubilc void StartTimer () {
timer.Start();
}
public void StopTimer () {
timer.Stop();
}
private void CheckForMessage (object source , ElapsedEventArgs e) {
if (MessageArrive != null) {
MessageArrived(this,"Hello");
}
}
public class Display {
public void DisplayMessage(MyClass myClass,string messge) { //这个方法匹配委托类型MessageHandler,用它来响应MessageArrived事件
...
}
}
MyClass myClass = new MyClass();
Display pDisplay = new Display ();
myClass.MessageArrived += new MessageHandler(pDisplay.DisplayMessage);
myClass.StartTimer();
34.匿名方法,除了定义事件处理方法外,还可以用匿名方法。匿名方法纯粹是为用作委托目的而创建的,如:
delegate (parameters) {
//...
}
其中parameters是一个参数列表,匹配正在实例化的委托的类型。对于匿名方法要注意,对于包含它们的方法来说,它们是局部的,可以访问这个区域内的局部变量。如果使用它作为变量,它就称为外部变量。外部变量在超出作用域时,是不会删除的,这与其他的局部变量不同,在使用它们的匿名方法被销毁时,外部变量才会删除。如果外部变量占用了大量内存,或者使用的资源在其他方面是比较昂贵的,就可能导致内存或性能问题。
MyClass myClass = new MyClass();
myClass.MessageArrived += delegate(MyClass myClass, string message) { //省略了display类以及其方法
//...
}
myClass.StartTimer();
35.dynamic类型, dynamic类型的不同寻常之处在于,它仅在编译期间存在,在运行期间它会被System.Object类型替代。这是较细微的实现细节,但必须记住这一点。一旦有了动态变量就可以继续访问其成员,不管其存在不存在,都能通过编译,但如果不存在成员,执行该代码时会生成一个RuntimeBinderException类型的异常。一般情况下,应仅在动态类型是唯一的选项时使用它们,例如处理非.NET对象,处理Python、JavaScript和Ruby等语言创建的对象。
dynamic myDynamicVar;
myDynamicVar.DoSomething("Hello!"); //无论myDynanmicVar是否包含DoSomething,都能编译。
36.扩展方法(类似objective-c里的类别技术),可以扩展类型的功能,但无需修改类型本身。甚至可以使用扩展方法扩展不能修改的类型,包括在.NET Framework中定义的类型。例如:System.String等基本类型添加功能。
为了扩展类型的功能,需要提供可以通过该类型的实例调用的方法。为此创建的方法称为扩展方法,它可以带任意数量的参数,返回任意类型。要创建和使用扩展方法,必须:
(1)创建一个非泛型静态类。
(2)使用扩展方法的语法,给所创建的类添加扩展方法,作为静态方法。扩展方法必须是静态的;扩展方法必须包含一个参数,表示调用扩展方法的类型实例(这个参数这里称作实例参数);实例参数必须为扩展方法定义的第一个参数;除了this关键字之外,实例参数不能有其他修饰符。
(3)确保使用扩展方法的代码用using语句导入了包含扩展方法类的名称空间。
(4)通过扩展类型的一个实例调用扩展方法,与调用扩展类型的其他方法一样。
public static class ExtebsionClass {
public static <ReturnType> <ExtentionMethodName> (this <TypeToExtend> instance) {
...
}
}
导入包含静态类的名称空间后,就可以编写以下代码:
<TypeToExtend> myVar;
myVar.<ExtensionMethodName> ();
//还可以在扩展方法中包含需要的其他参数,并使用返回类型。上边的代码实际等同以下代码,但是更简洁:
<TypeToExtend> myVar;
ExtensionClass.<ExtensionMethodName> (myVar);
还可以定义在特定接口上执行的扩展方法,接着就可以给实现了该接口的任意类型使用该扩展方法。
扩展方法提供了在项目中重用实用代码库的一种方式。