/*
* ·对象初始化器
* ·var类型,类型推理
* ·匿名类型
* dynamic类型
* 命名和可选的方法参数
* 扩展方法
* Lambda表达式
*/
/*
* 初始化器
* 可以使用初始化器在创建对象或集合的同时初始化它们。
* 这两种初始化器都包括一个放在花括号中的代码块。
* 对象初始化器可以提供一个逗号分隔的属性名/值对列表,来设置属性值。
* 集合初始化器仅需要逗号分割的值列表。
* 使用对象删除化器时,还可以使用非默认的构造函数。
*/
/*
* 类型推理
* 声明变量时,使用var关键字允许忽略变量的类型。
* 但只有类型可以在编译期间确定时才会如此。
* 使用var没有违反C#的强类型化规则,因为var声明的变量只有一个类型。
*
*/
/*
* 匿名类型
* 对于用于结构数据存储的许多简单类型,定义类型是不必要的。而可以使用匿名类型,其成员根据用途来推断。
* 使用对象初始化器语法来地冠以匿名类型,每个设置的属性都定义为只读属性。
*
*/
/*
* 动态查找
* 使用dynamic关键字定义dynamic类型的变量,可以存储任意值,接着就可以使用一般的属性或方法语法来访问被包含的值的成员,
* 这些成员仅在运行期间检查。如果在运行期间,尝试访问一个不存在的成员,就会抛出一个异常。
* 这种动态的类型化显著简化了访问.Net类型或类型信息补能在编译期间获得的.Net类型的语法。但是,在使用时动态类型要谨慎,
* 因为无法在编译期间检查代码。
* 在IDynamicMetaObjectProvider接口,可以控制动态查找的行为。
*/
/*
* 可选的方法参数
* 可以提供多个方法重载,而无不是强制客户代码为很少使用的参数提供值。
* 另外,也可以把这些参数定义为可选参数。调用方法的客户代码就可以仅指定需要的参数。
*/
/*
* 命名的方法参数
* 客户代码可以根据位置或名称来指定方法的参数。
* 命名的参数可以按任意顺序指定。这尤其适用于和可选参数一起使用的场合。
*/
/*
* 扩展方法
* 可以为任意已有的类型定义扩展方法,而无需修改类型定义。
* 创建通用的扩展方法的另一种方式是创建可以通过特定接口使用的扩展方法
*/
/*
* Lambda表达式
* Lambda表达式实际上是定义匿名方法的一种快捷方式,且由额外的功能,例如隐式的类型化。
* 定义Lambda表达式时,需要使用逗号分隔的参数列表(如果没有参数,就是用空括号)、=>运算符
* 和一表达式。该表达式可以是放在花括号中的代码块。
* Lambda表达式至多可以有8个参数和一个可选的返回类型。
* Lambda表达式可以用Action、Action<>和Func<>委托类型来表示。
* 许多可用于集合的LINQ扩展方法都使用Lambda表达式参数。
*/
/*其他OOP技术:
* · ::运算符
* · 全局名称空间限定符
* · 如何创建定制异常
* · 如果使用事件
* · 如何使用匿名方法
*/
/*
* ::运算符:提供了另一种访问名称空间中类型的方式。
* 如果要使用一个名称空间的别名,但该别名与实际名称空间层次结构之间的界限不清晰,这将是必须的。
* 在这种情况下,名称空间层次结构优先于名称空间别名。
*
* using MyNamespaceAlias = MyRootNamespace.MyNestedNamespace;
*
* namespace MyRootNamespace
* {
* namespace MyNamespaceAlias
* {
* public class MyClass
* {}
* }
*
* namespace MyNestedNamespace
* {
* public class MyClass
* {}
* }
* }
*
* MyNamespaceAlias.MyClass
* 表示类是MyRootNamespace.MyNamespaceAlias.MyClass类,而不是MyRootNamespace.MyNestedNamespace.MyClass类。
* 也就是说,MyRootNamespace.MyNamespaceAlias名称空间隐藏了由using语句定义的别名。
* MyNestedNamespace.MyClass
* 表示MyRootNamespace.MyNestedNamespace.MyClass
* 也可以使用MyNamespaceAlias::MyClass
* 使用这个运算符会会迫使编译器使用由using语句定义的别名。
*
* ::运算符还可以和global关键字一起使用,它实际上是顶级根名称空间的别名。
* global::System.Collections.Generic.List
*/
/*
* 定制异常
* try...catch...finally
* 调试->异常->添加
*/
/*
* 事件:类似于异常,因为它们都是由对象引发(抛出),我们可以提供代码来处理事件。
* 事件必须订阅:订阅一个事件的含义是提供代码,在事件发生时执行这些代码,它们称为事件处理程序。
*
* 单个事件可供多个处理程序订阅,在该事件发生时,这些处理程序都会被调用,其中包括引发该事件的
* 对象所在的类中的事件处理程序,但事件处理程序也可能在其他类中。
* 事件处理程序本身都是简单的方法。对事件处理方法的唯一限制时它必须匹配于事件所需求的返回类型和参数。
* 关键字:+=
* 在定义事件前,必须先定义一个委托类型,以用于该事件,这个委托类型指定了事件处理方法必须拥有的返回类型和参数。
*
*/
/*
* 匿名方法
* 它不是某个类上的方法,而纯粹时为用作委托目的而创建的。
* 对于匿名方法要注意,对于包含它们的代码块来说,它们是局部的,可以访问这个区域内的局部变量。
* 如果使用这样一个变量,它就成为外部变量。外部变量在超出作用域时,是不会删除的,这与其他局部变量不同,
* 在使用它们的匿名方法被销毁时,外部变量才会删除。
*
*/
/*
* 泛型:对应C++中的模板
*
* 泛型类是以实例化过程中提供的类型或类为基础建立的,可以毫不费力地对
* 对象进行强类型化
*
*
* CollectionClass items = new CollectionClass();
* items.Add(new ItemClass());
* 上述代码可以用
* CollectionClass items = new CollectionClass();
* items.Add(new ItemClass());
* 代替。
*
* 尖括号语法就是把类型参数传送给泛型类型的方式。
*
* 泛型允许灵活地创建类型,处理一种或多种特定类型的对象,这些类型是在实例化时确定的,
* 否则就使用泛型类型。
*/
/*
* .Net Framework提供的泛型包括System.Collections.Generic名称空间中的类型
*/
/*
* 可空类型
* 值类型(大多数基本类型,例如int,double和所有的结构)
* 引用类型(string和所有的类)
* 值类型必须包含一个值,它们可以在声明之后、赋值之前,在未赋值的状态下存在,
* 但是不能以任何方式使用。而引用类型可以是null。
*
* 有时让值类型为空是很有用的(尤其是处理数据库时),泛型使用System.Nullable类型
* 提供了值类型为空的一种方式。例如:
* System.Nullable nullableInt;
* nullableInt变量可以用友int变量能包含的任意值,还可以拥有值null。
* 可以编写:nullableInt = null;
* 等价于:nullableInt = new System.Nullable();
*
* 但与其他任何变量一样,无论是初始化为null(使用上面的语法),还是通过给它赋值来初始化,都不能在
* 初始化之前使用它。
*
* 可以像测试引用类型一样,测试可空类型,判断是否为null
* if(nullableInt == null)
* {
* ...
* }
* 也可以使用HasValue属性
* 如果HasValue是true,说明Value属性有一个非空值
* 如果HasValue是false,说明变量被赋予了null,访问
* Value属性会抛出System.InvalidOperationException类型的异常。
* if(nullableInt.HasValue)
* {
* ...
* }
* HasValue不适用于引用类型,即使引用类型有一个HasValue属性,也不能使用这种方法,
* 因为引用类型的变量值为null,就表示不存在对象,当然就不能通过对象来访问这个属性,否则
* 会抛出一个异常。
*
* 声明可空类型的变量不使用上述语法,而使用下面的语法:
* int? nullableInt;
*
* int?是System.Nullable的缩写。
*/
/*
* 正确:
* int? op1 = 5;
* int? result = op1 * 2;
*
* 错误:
* int? op1 = 5;
* int result = op1 * 2;
*
* 必须进行显示转换
* int? op1 = 5;
* int result = (int)op1 * 2;
*
* 或者使用Value属性
* int? op1 = 5;
* int result = op1.Value * 2;
* 如果op1是null,则会产生System.InvalidOperationException类型的异常。
*/
/* int? op1 = null;
* int? op2 = 5;
* int? result = op1 * op2;
* 对于除了bool?之外的所有简单可空类型,上述操作的结果都是null,
*/
/*
* ??运算符
* 为了进一步减少处理可空类型所需要的代码量,使可空变量的处理变的更简单,可以使用??运算符。
* 这个运算符称为 空接合运算符,是一个二元运算符,允许给可能等于null的表达式提供另一个值。
* 如果第一个操作数不是null,该运算符就等于第一个操作数,否则,运算符就等于第二个操作数。
* 一下两个表达式的作用是相同的:
* op1??op2
* op1 == null?op2:op1;
*
* op1可以是任意可空表达式,包括引用类型和可空类型。
* 因此如果可空类型是null,就可以使用??运算符提供要使用的默认值。
* int? op1 = null;
* int result = op1 * 2 ?? 5;
* 在处理中放入int类型的变量result不需要显示转换,??运算符会自动处理这个转换。
* 也可以:
* int? result = op1 * 2 ?? 5;
*/
/*
* System.Collections.Generic名称空间其中两个类型
* ·List :T类型对象的集合
* ·Dictionary :与K类型的键值相关的V类型的项的集合
*
* List
* List泛型集合类型更加快捷、更易使用;不必从CollecitonBase中派生一个类,然后实现需要的方法。
* 它的另一个好处是正常情况下需要实现的许多方法(例如Add())已自动实现了。
*
* 创建T类型对象的集合需要如下代码:
* List myCollection = new List();
* 这个语法实例化的对象支持如下方法和属性
* int Count;
* void Add(T item);
* void AddRange(IEnumberable)
* IListAsReadOnly()
* int Capacity
* void Clear()
* bool Contains(T item)
* void CopyTo(T[] array,int index)
* IEnumberatorGetEnumberator()
* int IndexOf(T item)
* void Insert(int index,T item)
* bool Remove(T item)
* void RemoveAt(int index)
*
* List还有一个Item属性,允许进行类似于数组的访问,如下:
* T itemAtIndex2 = myCollectionOfT[2];
*/
/*
* List排序
* 泛型接口IComparer和IComparable
* int IComparable.CompareTo(T otherObj)
* bool IComparable.Equals(T otherObj)
* int IComparer.Compare(T objectA,T objectB)
* bool IComparer.Equals(T objectA,T objectB)
* int IComparer.GetHashCode(T objectA)
*/
/*两个可以使用的泛型委托类型:
* ·Comparision:这个委托类型用于排序方法,其返回类型和参数如下:
* int method(T objectA,T objectB)
* ·Predicate:这个委托类型用于搜索方法,其返回类型和参数如下:
* bool method(T targetObject)
*
*
* Comparison sorter = new Comparison(VectorDelegates.Compare)
* route.Sort(sorter);
* 可以用一下替换:
* route.Sort(VectorDelegates.Compare);
* 这样就不需要显示引用Comparison类型了,实际上,仍会创建这个类型的一个实例,
* 但它是隐式创建的。
* 对VectorDelegates.Compare()的引用(没有括号)成为方法组。
* 在许多情况下,都可以使用方法组以这种方式隐式地创建委托,使代码变得更容易读取。
*/
/*
* Dictionary
* 这个类型可以定义键/值对的集合。
* 这个类需要实例化两个类型,分别用于键和值,以表示集合中的各个项。
* 调用示例:
* foreach(string key in things.Keys)
* {
* Console.WriteLine(key);
* }
*
* foreach(int value in things.Values)
* {
* Console.WriteLine(value);
* }
*
* foreach(KeyValuePair thing in things)
* {
* Console.WriteLine("{0} = {1}",thing.Key,thing.Value);
* }
*
* 对于Dictionary:每一项的键都必须是唯一的。
* 如果要添加的项的键与已有项的键相同,就会抛出ArgumentException异常。
* Dictionary允许把IComparer接口传递给其构造函数,如果要把自己的类
* 用作键,且它们不支持IComparable或IComparable接口,或者要使用非默认的过程比较对象,
* 就必须把IComparer接口传递给其构造函数。
*/
/*
* 定义泛型类型
* 语法:
* class MyGenericClass
* {
* ...
* }
* 其中T可以是任意标识符,只要遵循通常的C#命名规则即可。
* 泛型类可以在其定义中包含任意多个类型,它们用逗号分隔开。
* class MyGenericClass
* {
* ...
* }
* 定义这些类型后,就可以在类定义中项使用其他类型那样使用它们。
* 可以把它们用作成员变量的类型、属性或方法等成员的返回类型以及方法变元的
* 多参数类型等。
* class MyGenericClass
* {
* private T1 innerT1Object;
* public MyGenericClass(T1 item)
* {
* innerT1Object = item;
* }
*
* public T1 InnerT1Object
* {
* get
* {
* return innerT1Object;
* }
* }
* }
*
* 注意:不能假定类提供了什么类型。
*
* 下述代码会出现异常。
* public MyGenericClass()
* {
* innerT1Object = new T1();
* }
*
* T1假设:可以把它看作继承自System.Object的类型或可以封箱到System.Object中的类型。
*
* 注意:在比较为泛型类型提供的类型值与null时,只能使用运算符==和!=。
*
* public bool Compare(T1 op1,T1 op2)
* {
* if(op1 != null && op2 != null)
* {
* return true;
* }
*
* return false;
* }
*/
/*
* default关键字
* 要确定用于创建泛型类实例的类型,需要了解一个最基本的情况:
* 它们时引用类型还是值类型。
*
* 如果不知道这个情况,就不能使用下面的代码赋予null值
* pubic MyGenericClass()
* {
* innerT1Object = null;
* }
*
* 所有使用default关键字
* public MyGenericClass()
* {
* innerT1Object = default(T1);
* }
* 其结果是:如果innerT1Object是引用类型,就赋予null值;
* 如果是值类型,就赋值默认值。
*/
/*
* 约束类型
* 前面用于泛型类的类型成为无绑定类型,因为没有对它们进行任何约束。
* 而通过约束类型,可以限制可用于实例化泛型类的类型。
* 代码是正确的:MyGenericClass = new MyGenericClass();
* 无法编译:MyGenericClass = new MyGenericClass();
*
* 在类定义中,可以使用where关键字实现:
* class MyGenericClass where T :constraint
* {
* ...
* }
* 其中constraint定义了约束。
* 可以用这种方式提供许多约束,各个约束间用逗号隔开:
* class MyGenericClass where T :constraint1,constraint2
* {
* ...
* }
*
* 还可以使用多个where语句,定义泛型类需要的任意类型或所有类型上的约束:
* class MyGenericClass where T1 :constrait1 where T2 : constraint2
* {
* ...
* }
*
* 约束必须出现在继承说明符后面:
* class MyGenericClass : MyBaseClass, IMyInterface
* where T1 :constraint1 where T2 :constraint2
* {
* ...
* }
*
* 一些可用的约束:
* struct 类型必须是值类型
* class 类型必须是引用类型
* base-class 类型必须是基类或继承自基类。可以给这个约束提供任意类名
* interface 类型必须是接口或实现了接口
* new() 类型必须是一个公共的无参数构造函数
* 如果用new()用作约束,它就必须是为类型指定的最后一个约束。
*
* 可以通过base-class约束,把一个类型参数用作另一个类型参数的约束:
* class MyGenericClass where T2 : T1
* {
* ...
* }
* 其中,T2必须与T1的类型相同或者继承自T1。
* 这称为:裸类型约束:表示一个泛型类型参数用作另一个类型参数的约束。
* 类型约束不能循环。
*
*/
/*
* 从泛型类继承
* 1、如果某个类型所继承的基类型中受到了约束,该类型就不能解除约束。
* 即:类型T所继承的基类型中使用时,该类型必须受到至少与基类型相同的约束。
* 2、如果继承了一个反类型,就必须提供所有必须的类型信息,这可以使用其他泛型
* 类型参数的形式来提供,也可以显示提供。这个也适用于继承了泛型类型的非泛型类。
* 如:
* 正确:
* public class Cards : List,ICloneable
* {}
*
* 错误:
* public class Cards : List,ICloneable
* {}
*/
/*
* 泛型运算符
* 在C#中,可以像其他方法一样进行运算符重写,这也可以在泛型类中实现此类重写
* public static implicit operator List(Farm farm)
* {
* ...
* }
* public static Farm operator +(Farm farm1,List farm2)
* {
* ...
* }
*
*
* 泛型结构
* public struct MyStruct
* {
* public T1 item1;
* public T2 items;
* }
*/
/*
* 定义泛型接口
* 位于:Systems.Collections.Generic名称空间
* 定义泛型接口与定义泛型类所使用的技术相同
*
* interface MyFarmingInterface where T : Animal
* {
* bool AttemptToBreed(T animal1,T animal2);
*
* T OldestInHerd{get;}
* }
* 泛型接口继承规则与泛型类相同:
* 如果继承了一个基泛型接口,就必须遵循“保持基接口泛型类型参数的约束”等规则
*/
/*
* 定义泛型方法
* 在泛型方法中,返回类型和/或参数类型由泛型类型参数来确定
* pubic T GetDefault()
* {
* return default(T);
* }
* 方法调用:
* int myDefaultInt = GetDefault();
*
* 可以通过非泛型类来实现泛型方法:
* public class Defaulter
* {
* public T GetDefault()
* {
* return default(T);
* }
* }
*
* 但如果类时泛型的,就必须为泛型方法类型使用不同的标识符
* 下面的代码会编译失败:
* public class Defaulter
* {
* public T GetDefault()
* {
* return default(T);
* }
* }
* 必须重命名方法或类使用的类型T。
*
* 泛型方法参数可以采用与类相同的方式使用约束,在此可以使用任意的类类型参数。
* 例如:
* public class Defaulter
* {
* public T2 GetDefault() where T2 : T1
* {
* return default(T2);
* }
* }
* 其中,为方法提供的类型T2必须与给类提供的T1相同,或继承自T1。
* 这是约束泛型方法的常用方式。
*/
/*
* 定义泛型委托
* 要定义泛型委托,只需声明和使用一个或多个泛型类型参数,例如:
* public delegate T1 MyDelegate(T1 op1,T2 op2) where T1 : T2;
*/
/*
* 变体
* 变体是协变和抗变的统称。类似与多态性。
*
* 下面代码是成立的:
* Cow myCow = new Cow("Geronimo");
* Animal myAnimal = myCow;
*
* 下面代码是不能编译的:
* IMethaneProducer cowMethaneProducer = myCow;
* IMethaneProducer animalMethaneProducer = cowMethaneProducer;
* 失败原因是;
* 因为泛型类型的所有类型参数都是不变的,但是可以在泛型接口和泛型委托上定义
* 变体类型参数。
*
* 为了是上述代码工作,IMethaneProducer接口类型参数T必须是协变的。
* 有了协变的类型参数,就可以在IMethaneProducer和IMethaneProducer
* 之间建立继承关系,这样一种类型的变量就可以包含另一种类型的值,这与多态性类似。
*
* 抗变和协变方向相反。
* 抗变不能项协变那样,把泛型接口值放在使用基类型的变量中,而可以把该接口放在使用派生
* 类型的变量中,例如:
* IGrassMuncher cowGrassMuncher = myCow;
* IGrassMuncher superCowGrassMuncher = cowGrassMuncher;
*
* 抗变:可以把泛型接口值放在使用派生类型的变量中。父类放到子类,参数只能做方法参数,不能做返回值。
* 协变:可以把泛型接口值放在使用基类型的变量中。子类放到父类,参数只能做返回值或属性get访问器。
*/
/*
* 协变:关键字out
* public interface IMethaneProducer
* {
* ...
* }
* 协变类型参数只能用作方法的返回值或属性get访问器。
* 例子:IEnumberable接口
*/
/*
* 抗变:关键字:in
* pulic interface IGrassMuncher
* {
* ...
* }
* 抗变类型参数只能用作方法参数,不能用作返回类型。
* 例子:IComparer
*/
/*
* ·如何定义和使用集合
* ·可以使用的不同类型的集合
* ·如何比较类型,如何使用is运算符
* ·如何比较直,比较重载运算符
* ·如何定义和使用转换
* ·如何使用as运算符
*/
/*
* OOP高级技术:
* ·集合:可以使用集合来维护对象组
* ·比较:处理对象时,常常需要比较他们
* ·转换:如何定制类型转换,以满足自己的要求
*/
/*
* 数组:一旦创建好数组,他们的大小就固定,不能在现有数组的末尾田间新项
* OOP技术可以创建在内部执行大多数此类处理的类,简化了是使用项列表或数组的代码
* C#中数组实现为System.Array类的实例
* 他们只是集合类中的一种类型
* 集合类一般用于处理对象列表,功能比简单数组要多
* 功能大多是通过实现System.Collections命名空间中的接口而获得的
* 因此集合的语法已经标准化了。
*/
/*
* 集合的功能可以通过接口实现:
* 优点:
* ·定制的集合类可以强类型化;
* ·提供专用的方法
*/
/*
* System.Collections名称空间中的几个接口提供了基本的组合功能:
* ·IEnumerable可以迭代集合中的项
* ·ICollection(继承于IEnumerabled)可以获取集合中项的个数,并能把项赋值到一个简单的数组类型中。
* ·IList(继承于IEnumerable和ICollection)提供了集合的项列表,允许访问这些项,并提供其他一些与项列表相关的基本功能。
* ·IDictionary(继承于IEnumerable和ICollection)类似于IList,但提供了可以通过键值(而不是索引)访问的项列表。
*/
//System.Array类实现IList、ICollection和IEnumerable,但不支持IList一些高级功能,它表示大小固定的项列表。
//System.Collections.ArrayList实现了IList,ICollection和IEnumerable接口,可以用于表示大小可变的项列表
/*
* 除了IList集合外,集合还可实现类似的IDictionary接口,允许通过关键值(如字符串名)进行索引,而不是通过一个索引。
* 这个也可以使用索引符来完成,但这次的索引符参数是与存储的项相关联的关键字,而不是int索引
*
* 可以使用一个基类简化IDictionary接口的实现
* DictionaryBase:实现了IEnumerable和ICollection,提供了对任何集合都相同的基本集合处理功能
* DictionaryBase与CollectionBase一样,实现通过其支持的接口获得的一些成员(不是全部成员)
* DictionaryBase也实现Clear和Count成员,但不实现RemoveAt()。这是因为RemoveAt()是IList接口的一个方法,不是IDictionary接口的方法。
* IDictionary有一个Remove()方法
*/
/*
* 迭代器
* IEnumerable接口负责使用foreach循环
* 重写使用foreach循环的方式,或者提供定制的实现方式,并不一定简单
* foreach循环中,迭代集合cllectionObject的过程如下:
* 1>调用collectionObject.GetEnumerator(),返回一个IEnumeator引用
* 2>调用所返回的IEnumerator接口的MoveNext()方法
* 3>如果MoveNext()方法返回true,就是用IEnumberator接口的Current属性获取对象的一个引用,用于foreach循环
* 4>重复前面两步,直到MoveNext()方法返回false为止,此时循环停止。
*
* 使用迭代器将有效地在后台生成许多代码,正确的完成所有任务。
* 迭代器:它是一个代码块,按顺序提供了要在foreach循环中使用的所有值。一般情况下,这个代码块是一个方法,
* 但也可以使用属性访问器和其他代码作为迭代器。
*
* 无论代码块是什么,其返回类型都是有限制的。
* 与期望正好相反,这个返回类型与所枚举的对象典型不同。
* 例如,在表示Animal对象集合的类中,迭代器块的返回类型不可能是Animal。
* 两种可能的返回类型是:接口类型IEnumerable和IEnumerator
* 使用这两个类型场合是:
* ·如果要迭代一个类,可使用方法GetEnumerator(),其返回类型是IEnumerator
* ·如果要迭代一个类成员,例如一个方法,则使用IEnumerable
* 在迭代器块中,使用yield关键字选择要在foreach循环中使用的值。
* 语法:yield return ;
* 多个返回值类型需一致。
* yield 返回object类型的值
* yield 语句可以返回任意类型
*
* 中断信息返回:yield break;
*/
/*
* 浅表复制:MemberwiseClone();
* 深复制:实现ICloneable接口,该接口有一个方法Clone();
*/
/*
* 封箱拆箱:
* 封箱:把值类型转换为System.Object类型或者转换为由值类型实现的接口类型;
* 拆箱:是相反的过程
* 注意:在访问值类型内容前,必须进行拆箱
*
* is运算符:是否可转换为
* is运算符并不是说明对象是某种类型的一种方式,而是可以检查对象是否是给定类型,或者是
* 否可以转换为给定类型,如果是,这个运算符就返回true.
* GetType()方法和typeof()运算符很难做到这一点。
* 语法: is
* ·如果是一个类类型,而也是该类型,或者它继承了该类型,或者它可以封箱到该类型中,则结果为true.
* ·如果是一个接口类型,而也是该类型,或者它是实现该接口的类型,则结果为true.
* ·如果是一个值类型,而也是该类型,或者它可以拆箱到该类型中,则结果为true.
*/
/*
* 值比较:
* ·运算符重载
* ·使用IComparable和IComparer接口,是对集合中对象进行排序的一种绝佳方式。
*
* 要重载运算符可以给类添加运算符类型成员,它们必须是static。
* 一些运算符有多种用途(如-运算符就有一元和二元两种功能),因此还只能怪了要处理多个操作数,以及这些操作数的类型。
* 应注意不要把签名相同的运算符添加到多个类中。
* 如果混合了类型,操作数的顺序必须按照运算符重载的参数顺序相同。
* 可以重载的运算符:
* ·一元运算符:+,-,!,++,--,true,false
* ·二元运算符:+,-,*,/,%,|,^,<<,>>
* ·比较运算符:==,!=,<,>,<=,>=
* 不能重载运算符:
* +=,&&,||,
* 成对重载
* <,>
*
* ==,!=常常需要重写Object.Equals()和Object.GetHashCode(),因为这两个函数也可以用于比较对象。
*
* 如果重载true或false运算符,就可以在布尔表达式中使用类,例如,if(op1){}
*
*
*
* IComparable和IComparer接口是.Net Framework中比较对象的标准方式。
* IComparable:在要比较的对象的类中实现,可以比较该对象和另一个对象。
* IComparer:在一个单独的类中实现,可以比较任意两个对象。
*
* IComparable提供了一个方法:CompareTo()这个方法接受一个对象,返回一个整型。
* ICompare提供了一个方法:Compare()这个方法接受两个对象,返回一个整型结构;
*
* .Net Framework在类Comparer上提供了IComparer接口的默认实现方式
* Comparer.Default.Compare(ObjectA,ObjectB);
* 使用Comparer时,必须使用可以比较的类型,例如,试图比较string和int就会发生异常
* Comparer使用注意事项:
* ·检查传送给Comparer.Compare()的对象,看看他们是否支持IComparable,如果支持,就是用该实现代码。
* ·允许使用null值,它表示“小于”其他对象。
* ·字符串根据当前文化来处理,要根据不同的文化(或语言)处理字符串,Comparer类必须使用其构造函数
* 进行实例化,一边传送制定所使用的文化的System.Globalization.CultureInfo对象。
* ·字符串在处理时要区分大小写。如果要以不缺分大小写的方式来处理他们,就需要使用CaseInsensitiveCompare类。
*/
/*使用IComparable和IComparer接口对集合排序
* ArrayList.Sort();
*
* 在给ArrayList填充了简单类型时,例如整数或字符串,就会进行默认的比较。对于自己的类,
* 必须在类定义中实现IComparable,或者创建一个支持IComparable的类,来进行比较。
*
* System.Collection名称空间中的一些类,包括CollectionBase都没有提供排序方法。
* 如果要对派生于这个类的集合排序,就必须多做一些工作,自己给内部的List集合排序。
*/
/*
* 转换
* 到目前为止,在需要把一种类型转换为另一种类型时,使用的都是类型转换。
* 而这并不是唯一的方式。
* 在计算过程中,int可以隐式转换为long或double,采用相同的方式还可以定义所创建的类
* (隐式或显式)转换为其他类的方式。
* 为此,可以重载转换运算符。
* as 运算符:一般适用于引用类型的转换。
*
* implicit隐式
* explicit显示
* 在显示转换中使用了checked关键字,如ConvClass1 op2 = (ConvClass1)op1;
* 则上述代码将会产生一个异常,因为op1的val属性太大,不能放在op2的属性
*
* as运算符 :把一种类型转换为指定的引用类型
* 语法: as
* 这只适用于下列情况:
* ·的类型是类型
* ·可以隐式转换为类型
* ·可以封箱到类型中
* 如果不能从转换为,则表达式的结果就是null.
*
* 基类到派生类的转换可以使用显示转换来进行,但这并不总是有效的。
*
* as运算符非常有用,因为
* as代码会把null赋予对象时,不会抛出异常。可以在类型转换中根据是否为空来判断,而不会报错。
*/
/*
* 所有成员都有自己的访问级别:
* public - 成员可以由任何代码访问
* private - 成员只能由类中的代码访问(无修饰符时默认为)
* internal - 成员只能由定义他的程序集(项目)内部的代码访问
* protected - 成员只能由类或派生类中的代码访问
* protected internal 只能由程序集中的派生类的代码来访问
* */
//.NetFramework中的公共字段以PascalCasing形式命名
//私有字段通常使用camelCasing形式命名
//如果使用static表示这个方法只能通过类来访问,不能通过对象实例来访问
/*
* 可以在方法定义中使用
* virtual - 方法可重写
* abstract - 方法必须在非抽象的派生类中重写(只用于抽象类中)
* override - 方法编写了一个基类方法(如果方法被重写,就必须使用改关键字)
* extern - 方法定义放在其他地方
*/
/*
*属性的基本结构包括标准的
* 可访问修饰符(public、private、protected),后跟类名、属性名和get块或set块,其中包含属性处理代码
* 属性可以使用virtual、override和abstract关键字但这几个关键字不能用于字段
*/
//.Net中公共属性也以PascalCasing方式命名
//部分方法也可以是静态的,但他们总是私有的,且不能有返回值
//他们使用的任何参数都不能是out参数,但可以是ref参数
//部分方法也不能使用vitual、abstract、override、new、sealed和extern修饰符
/*
* 前缀修饰
1、缺省 internal 内部的:只有当前项目中的代码才可以访问
2、public 公共的:可以由其他项目中的代码访问
1、abstract 抽象的:不能实例化,只能继承
2、sealed 密闭的:不能继承
所有类的根都是System.Object
*/
//继承:C#中只能有一个基类,如果继承一个抽象类,则必须实现所继承的所有抽象成员(除非派生类也是抽象的)
//继承基类,实现接口顺序:基类,接口,接口,……接口
//不能在接口中使用abstract和sealed
//接口继承:接口,接口,……,接口