读书笔记之《C#入门经典》提高篇

       总结一些在学习《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>支持的方法和属性如下表:(NTList<T>还支持Item属性,允许进行类似于数组的访问)

说明

int Count

该属性给出集合中项的个数

void Add(T item)

把一个项添加到集合中

void AddRange(IEnumerable<T>)

把多个项添加到集合中

IList<T> AsReadOnly()

给集合返回一个只读接口

int Capacity

获取或设置集合可以包含的项数

void Clear()

删除集合中所有项

bool ContainsT 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 methodT 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对象,处理PythonJavaScriptRuby语言创建的对象。

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);

       还可以定义在特定接口上执行的扩展方法,接着就可以给实现了该接口的任意类型使用该扩展方法。

       扩展方法提供了在项目中重用实用代码库的一种方式。


猜你喜欢

转载自blog.csdn.net/eric_XJJ/article/details/8632159
今日推荐