- 建议使用stringbuilder和format对字符串进行拼接,避免额外的内存开销和避免拆箱和装箱操作;
- 操作符的重载Operate ----实现类对象可以像值类型那样进行操作符的操作;
- 创建对象时考虑是否实现比较器(IComparable,IComparer)---实现对象的排序方式;尽量不适用非泛型集合类,因为在使用非泛型集合类的过程中可能会发生转型,影响性能;
IComparable一般作为对象排序的默认比较器;
IComparer 一般是作为为Sort方法提供非默认的比较器;
-
重写equals方法后一定要重写GetHashCode方法----hashCode方法也是可以用来比较两个对象是否相等的。但是我们很少使用,应该说是很少直接使用。hashCode方法返回的是一个int值,可以看做是一个对象的唯一编码,如果两个对象的hashCode值相同,我们应该认为这两个对象是同一个对象。
-
GetHashCode 基于适合哈希算法和诸如哈希表的数据结构的当前实例返回一个值。 两个相等的同类型对象必须返回相同的哈希代码,才能确保以下类型的实例正确运行:
- T:System.Collections.HashTable
- System.Collections.SortedList
- T:System.Collections.Generic.Dictionary
- T:System.Collections.Generic.SortDictionary
- T:System.Collections.Generic.SortList
- T:System.Collections.Specialized.HybredDictionary
- System.Collections.Specialized.ListDictionary
- System.Collections.Specialized.OrderedDictionary
- 实现 T:System.Collections.Generic.IEqualityComparer 的类型
重写equals方法时需要重写hashCode方法,主要是针对以上集合类型的使用:
a: 以上集合类型存放的对象必须是唯一的;
b: 集合类判断两个对象是否相等,是先判断equals是否相等,如果equals返回TRUE,还要再判断HashCode返回值是否ture,只有两者都返回ture,才认为该两个对象是相等的。
重写GetHashCode的原则很简单,只要能保证两个相等的同类型对象返回相同的哈希代码就OK了;
重写Equals方法的同时,也应该实现一个类型安全的的接口IEquatable<T>
不建议使用值类型对象的GetHashCode函数返回值来作为HashTable对象的Key;
不管是值类型还是引用类型,要保证产生HashCode的成员不能被修改;
减少产生相同HashCode的一个通用而且成功的算法就是XOR(异或)运算,对一个类型内的所有字段的散列再进行异或后返回。
/// <summary> /// 重写Equals方法的同时,也应该实现一个类型安全的的接口IEquatable<T> /// 不建议使用值类型对象的GetHashCode函数返回值来作为HashTable对象的Key; /// 不管是值类型还是引用类型,要保证产生HashCode的成员不能被修改; /// </summary> class Person : IEquatable<Person> { public string IDCode { get; private set; } public Person(string idCode) { this.IDCode = idCode; } public override bool Equals(object obj) { return IDCode == (obj as Person).IDCode; } public override int GetHashCode() { //减少不同字符串产生相同的HashCode的几率 return (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "#" + this.IDCode).GetHashCode(); } public bool Equals(Person other) { return IDCode == other.IDCode; } }
- 使用dynamic来简化反射实现--参考 C#动态编程
-
多数情况下使用foreach实现对集合的遍历-------集合类必须继承IEnumerable接口且必须实现GetEnumerator方法,这个方法返回一个IEnumerator(迭代器);
Foreach用于遍历一个继承了IEnumerable或IEnumerable<T>接口的集合元素,简化了迭代器模式的语法;并且自动将代码置于try-finally块,若类型实现了IDispose接口将会在finally块总自动执行Dispose方法;
缺点:foreach不支持循环时对集合的增删操作;
- 协变:让返回值类型返回比声明的类型派生程度更大的类型;协变支持的两种方式:第一种使用泛型参数<T>兼容泛型接口的不可变性
class Program { static void Main(string[] args) { ISalary<Programmer> s=new BaseCounter<Programmer>(); PrintSalary(s); } static void PrintSalary<T> (ISalary<T> s) //泛型参数<T>兼容泛型接口的不可变性 { s.Pay(); } } interface ISalary<T> { void Pay(); } class BaseCounter<T> : ISalary<T> { public void Pay() { Console.WriteLine("Pay Salary Counter! "); } } class Employee { public string Name{get;set;} } class Programmer : Employee {} class Manager: Employee {}
class Program { static void Main(string[] args) { ISalary<Programmer> s=new BaseCounter<Programmer>(); PrintSalary(s); } static void PrintSalary(ISalary<Employee> s) { s.Pay(); } } interface ISalary<out T> { void Pay(); }
class BaseCounter<T> : ISalary<T>
{
public void Pay()
{
Console.WriteLine("Pay Salary Counter! ");
}
}委托协变:使用out 关键字扩展协变的可变性
public delegate T GetElegaHandle<T>(string name);//不可用于可变性 public delegate T GetElegaHandle<out T>(string name);//可用于可变性
- 逆变 :指方法的参数可以是委托或泛型接口的参数类型的基类。
//引入接口的逆变 public interface IMyComparable<in T> { int ComPare(T other); } public class Employee : IMyComparable<Employee> { public string Name { get; set; } public int ComPare(Employee other) { return Name.CompareTo(other.Name); } } class Programmer : Employee, IMyComparable<Programmer> { public int ComPare(Programmer other) { return Name.CompareTo(other.Name); } } [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Programmer programmer = new Programmer() {Name="Make" }; Manager manager = new Manager() { Name = "pzi" }; Test(programmer, manager);//如果不为IMyComparable接口的泛型参数T指定in关键字,将会导致编译出错 } static void Test<T>(IMyComparable<T> m, T n) { }
- 委托和事件
C#中使用事件需要的步骤: 1.创建一个委托 2.将创建的委托与特定事件关联(.Net类库中的很多事件都是已经定制好的,所以他们也就有相应的一个委托,在编写关联事件处理程序--也就是当有事件发生时我们要执行的方法的时候我们需要和这个委托有相同的签名) 3.编写事件处理程序 4.利用编写的事件处理程序生成一个委托实例 5.把这个委托实例添加到产生事件对象的事件列表中去,这个过程又叫订阅事件 C#中事件产生和实现的流程: 1.定义A为产生事件的实例,a为A产生的一个事件 2.定义B为接收事件的实例,b为处理事件的方法 3.A由于用户(程序编写者或程序使用者)或者系统产生一个a事件(例如点击一个Button,产生一个Click事件) 4.A通过事件列表中的委托对象将这个事件通知给B 5.B接到一个事件通知(实际是B.b利用委托来实现事件的接收) 6.调用B.b方法完成事件处理
public class A { public delegate void EventHandler(object sender); public event EventHandler a; public void Run() { Console.WriteLine("Trigger an event."); a(this); } } class B { public B(A a) { a.a += new A.EventHandler(this.b); } private void b(object sender) { Console.WriteLine("Received and handled an event." ); Console.Read(); } }
- 闭包对象:如果匿名方法(lambda表达式)引用了某个局部变量,具有数据共享和延迟的特性;编译器会自动将该局部变量添加到闭包对象(ClosureObject)中作为公共变量使用;
public void UseClosureObject() { List<Action> lists = new List<Action>(); for(int i=0;i<5; i++) { Action t = () => { Console.WriteLine(i.ToString()); }; lists.Add(t); } /* 等价于以上的Lambda表达式(匿名方法) ClosureObject closureObj = new ClosureObject(); for(closureObj.i=0;closureObj.i<5;closureObj.i++) { Action t = closureObj.ClosureFunc; lists.Add(t); } */ foreach (Action act in lists) { act(); } } class ClosureObject { public int i; public void ClosureFunc() { Console.WriteLine(i.ToString()); } }
- AssemblyInfo.cs文件中添加语句[assembly: InternalsVisibleTo("Host.VDC")],使其它程序集能访问 该程序集中Internal修饰的文件;
- 集合操作
对大型集合进行循环访问、转型或拆箱和装箱操作,要使用泛型集合;非泛型集合存在效率低及非类型安全的缺点;
数据结构:相互之间存在一种或多种特定关系的数据元素的集合;
线性集合是指元素具有唯一前驱和后驱的数据类型结构;
非线性集合是指有多个前驱和后驱的数据类型结构;
非线性集合分为层次群集和组群集;
层次群集FCL没有实现,组群集中的图在FCL也未实现;
直接存取:集合数据元素可以直接通过下标(index)来访问。优点添加元素高效,缺点插入和删除元素低效; ArrayList是一种动态数组,其容量可以动态扩充。在ArrayList内部封装的是一个System.Object类型的数组,对于当Arraylist操作值类型数据时会伴随着装箱操作带来性能的影响(如 Add、AddRange、Insert方法都是接受引用类型的参数),因此优先考虑使用List<T>;
顺序存取(线性表):可动态的扩大和缩小,在一片连续的区域中存储数据元素,通过对地址的引用来搜索元素,优点插入和删除元素效率高,缺点查找元素效率相对低;
有序集合:SortedList<T> SortedDictionary<TKey,TValue> SortedSet<T> 扩展于对应的类 List<T> Dictionary<TKey,TValue> Set<T> 散列(Hashtable):Hashtable类型的Add/Remove方法都接受Object类型的参数,会引起装箱操作;实现了IDictionary和ICollection接口
多线程集合类:命名空间 System.Collections.Concurrent
非泛型集合的线程安全通过锁定(lock)非泛型集合的SyncRoot属性来实现;
泛型通过锁定一个静态对象保证了在同一时刻只有一个线程操作集合元素;
public class Tip22CollectionThreadSafe { public ArrayList ArrayListSyncRoot = new ArrayList() { new Person() {Name="Rose",Age=19 }, new Person() {Name="Mike",Age=40 }, new Person() {Name="steve",Age=25 } }; public List<Person> GenericListLock = new List<Person>() { new Person() {Name="Rose",Age=19 }, new Person() {Name="Mike",Age=40 }, new Person() {Name="steve",Age=25 } }; private AutoResetEvent m_autoResetEvent = new AutoResetEvent(false); /// <summary> /// 非泛型集合的线程安全通过锁定(lock)非泛型集合的SyncRoot属性来实现 /// </summary> public void SyncRootThreadSafe() { m_autoResetEvent.Reset(); Thread thread1=new Thread(()=> { //确保等待thread2开始之后才运行下面的代码 m_autoResetEvent.WaitOne(); lock(ArrayListSyncRoot.SyncRoot) { foreach(Person person in ArrayListSyncRoot) { Console.WriteLine("thread1:" + person.Name); Thread.Sleep(1000); } } } ); thread1.Start(); Thread thread2 = new Thread(() => { //通知thread1可以执行代码 m_autoResetEvent.Set(); //沉睡1s确保删除操作是在集合迭代过程中 Thread.Sleep(1000); //锁定通过集合的互斥的机制保证了在同一时刻只有一个线程操作集合元素 lock (ArrayListSyncRoot.SyncRoot) { ArrayListSyncRoot.Remove(2); } }); thread2.Start(); } private static readonly object m_objectLock = new object(); /// <summary> /// 泛型通过锁定一个静态对象保证了在同一时刻只有一个线程操作集合元素 /// </summary> public void GenericLockThreadSafe() { m_autoResetEvent.Reset(); Thread thread1 = new Thread(() => { m_autoResetEvent.WaitOne(); lock (m_objectLock) { foreach (Person person in GenericListLock) { Console.WriteLine("t1:" + person.Name); Thread.Sleep(1000); } } } ); thread1.Start(); Thread thread2 = new Thread(() => { m_autoResetEvent.Set(); //沉睡1s确保删除操作是在集合迭代过程中 Thread.Sleep(1000); lock (m_objectLock) { GenericListLock.RemoveAt(2); Console.WriteLine("删除成功"); } }); thread2.Start(); } } public class Person { public string Name { get; set; } public int Age { get; set; } }
-
使用default(T)为泛型类型变量(T)指定初始值:
Public T Func<T>()
{
T t =default(T);
Return t;
}
- Override和new:使类型体系因为继承而呈现多态性;子类方法带有new关键字:该方法被定义为独立于基类的方法;子类方法带有Override关键字:重写基类的方法;
- 如果类型需要显式的释放资源,那么一定要继承IDispose接口;即使提供了显式释放方法,也应该在终结器中提供隐式清理;必要时将不再使用的对象引用赋值为null,特别是静态变量;
/// <summary> /// 如果类调用了非托管资源,或者需要显式的释放托管资源 /// CLR对于实现了Dispose模式的类型,每次在创建该类型的对象时,都会将该对象的一个指针放到终结列表中; /// GC在回收该对象的内存前,会首先将终结列表中的指针放到一个freachable队列中,同时分配专门的线程读取freachable队列,并调用对象的终结器; /// 这时对象才真正被标记为垃圾,并且在下一次调用GC时才会释放对象占用的内存 /// 可以看到Dispose模式的类型对象,至少需要两次GC才能真正回收掉对象内存 /// </summary> public class DisposeManageClass : IDisposable { //定义一个非托管资源 private IntPtr m_nativeResource = Marshal.AllocHGlobal(100); //定义一个托管资源 private ManagerIn m_manageResource = new ManagerIn(); private bool m_isDisposed = false;//标记资源是否被释放 /// <summary> /// 显示释放方法,减少一次垃圾回收调用 /// 实现IDisposable中的Dispose方法 /// </summary> public void Dispose() { //必须为true Dispose(true); //通知垃圾回收机制不再调用终结器(析构器) GC.SuppressFinalize(this); } /// <summary> /// 不是必要的,提供一个Close方法仅仅是为了更符合其他语言(如C++)的规范 /// </summary> public void Close() { Dispose(); } /// <summary> /// 析构器,必须有,防止忘记显式的调用Dispose方法 /// </summary> ~DisposeManageClass() { //必须为fase Dispose(false); } /// <summary> /// 使用受保护的虚方法,考虑类继承调用 /// 非密封类修饰用protected virtual /// 密封类修饰用private /// </summary> /// <param name="isDispose"></param> protected virtual void Dispose(bool isDispose) { if (m_isDisposed) return; //显式的调用Dispose需要手工的清理对象本身托管资源,而终结器调用不需要手工清理 if(isDispose) { //清理托管资源 if (m_manageResource != null) { //对象被dispose不表示该对象为null m_manageResource.Dispose(); m_manageResource = null; } } //清理非托管资源 if (m_nativeResource != IntPtr.Zero) { Marshal.FreeHGlobal(m_nativeResource); m_nativeResource = IntPtr.Zero; } //指示资源已释放 m_isDisposed = true; } public void SamplePublicMethod() { if (m_isDisposed) { throw new ObjectDisposedException("SampleClass", "SampleClass is disposed"); } //省略 } }
- 内存机制 ----值类型分配在栈(stack)上即直接包含他们的数据,而引用类型实例通常分配在托管堆(heap)上,变量保存引用类型实例的引用,由GC来控制回收;
每个变量或者程序都有其栈,不同的变量不能共有同一个栈地址,如上图myStruct和myStruct2保存了栈地址和数据,而myClass和myClass2仅保存了引用类型实例的引用地址; 引用类型嵌套值类型:值类型变量作为引用类型的成员变量时,它将作为引用类型实例数据的一部分,同该引用类型的其他字段都保存在托管堆上; 引用类型嵌套在值类型时,内存的分配情况为:该引用类型将作为值类型的成员变量,堆栈上将保存该成员变量的引用,而成员变量的实际数据还是保存在托管堆中;
- 特性:本质上是一个类,对程序中的元素进行标注(程序集,类型,字段,方法和属性等)提供关联的附加信息,attribute和元数据保存在一起,可以用来在运行时(在编译期初始化阶段通过反射的方式来获取附加信息)描述你的代码,或者在程序运行的时候影响应用程序的行为;
-
;;---