1.1. 析构方法
作用:释放对象
谁在使用:GC垃圾回收期在调用
(就算平时有调用也是间接的调用,通过Close|Dispose等方法)
① using 其实也是编译时编译成try-catch-finally{conn.Dispose();};只是写法上优雅(语法糖)
② 如果该对象有Dispose()就说明是非托管的资源,需要手动释放
③ Close()方法关闭对象,没有完全释放(可复用);Dispose()完全释放对象
垃圾回收机制(有些垃圾无法回收):
回收非托管资源---Windows窗口句柄、数据库链接、GDI对象、独占文件锁等等对象。 ApplicationContext,Brush,Component,ComponentDesigner,Container,Context,Cursor
FileStream,Font,lcon,lmage,Matrix,Object,OdbcDataReader,OleDBDataReader.Pen.
Regex,Socket,StreamWriter,Timer.Tooltip 等.
1--Dispose()需要实现IDisposable接口
2--Close()和Dispose()区别。
3--99%情况下不需要自己编写经典方法
public class DisposeTest : IDisposable
{
方式一 语法糖 using(FileStream fs = new FileStream(...))
#region 方式二 经典方式
private readonly IntPtr unmanagedResource;//非托管内存private readonly SafeHandle manageResource;//托管资源
public DisposeTest(){
unmanagedResource = Marshal.AllocHGlobal(sizeof(int));//分配非托管内存
managedResource = new SafeFileHandle(new IntPtr(), true);//创建托管资源
}
//模仿 回收非托管资源Socket、FileStream等实现Dispose方法,进行自己处理销毁这些资源
public void Dispose(){
Dispose(true);//调用处理方法
GC.SuppressFinalize(this);//让GC忽略
}
protected virtual void Dispose(boolean isManualDisposing){
ReleaseUnmanagedResource(unmanagerResource);//处理非托管资源
if(isManualDisposing){
ReleaseManagedResource(managedResource);//处理托管资源
}
}
private void ReleaseManagedResource(SafeHandle safeHandle){
if(safeHandle != null){
safeHandle.Dispose();
}
}
private void ReleaseUnmanagedResource(IntPtr intPtr){
Marshal.FreeHGlobal(intPtr);//释放非托管内存
}
//对象被销毁时,会自动调用析构方法,销毁回收非托管资源
~DisposeAction(){
Dispose(false);
}
#endregion
}
1.2. 虚方法
关键字:virtual+override
作用:允许子类/派生类重写,去实现不一样的功能
特点: 好维护
1.3. 抽象类
关键字:abstract
定义:一定要写在抽象类里面,而且不能new,不带方法体
使用场景:抽象方法强制性要求子类去实现
① 单继承,接口可以多继承
接口的关键字:interface
② 对象初始化新方式:
Person person = new Person(){
Age = 20,
Name = "Ant",
Phone = "123456"
}
1.4. 自定义泛型
① 快速创建构造方法的快捷方式:Alt + Enter 或 ctrl+.
② 内部对象类用struct结构?
③ 可以通过泛型属性(MyGeneric<int>右键菜单【open code in ILSpy】反编译查看源码)
安装该插件:扩展/管理扩展/联机(搜索ILSpy反编译工具)
④ 泛型的实现底层源码中用MyGeneric占位符=`反单引号(`1<!T>::t),调用时才会塞入真正类型MyGeneric替换掉占位符(`1<int32>::.ctor(!0)) 【这一点java的泛型只是语法糖,只保证安全性】
static void Main(string[] args)
{
//===============泛型自定义==================
MyGeneric<int> myGeneric = new MyGeneric<int>(1111);
myGeneric.Show();
//================泛型方法=================
Show("这是泛型方法");
Show(888888);//直接根据入参判断泛型类型
//================泛型接口================
Dictionary<TKey, TValue> : IDictonary<Tkey,TValue>,ICollection<KeyValuePair<TKey,TValue>>
}
public static void Show<T>(T t)
{
Console.WriteLine(t.ToString());
}
class MyGeneric<T>
{
private T t;
public MyGeneric(T t){//快捷方式:Alt + Enter 或 ctrl+.
this.t =t;
}
public void Show(){
Console.WriteLine(t);
}
}
① 点击项目右键【生成】,其实点击顶部工具栏的【启动】也会触发生成(编译过程)
② 生成DLL/EXE文件
1、项目右键【在文件资源管理器中打开文件夹】->项目文件夹下/bin/debug/文件夹下->编译后此处(控制台程序)就会生成exe文件;如果是类库的话,会生成dll文件
③ 需要通过CLR/JIT来继续编译成机器码(0|1)*;exe由中间语言IL构成(类似class字节码文件)
1.5. C#泛型的约束
泛型约束where T:XX
[1] new()约束--表示T类型只接收带有一个无参数的构造函数
[2] struct值类型约束(show<T>(T t) where T:struct)
[3] class引用类型约束
[4]自定义类型约束(show<T>(T t) where T:Student)(基类型约束、接口类型约束),基类约束,只能有一个而且要放前面(where D:Student,Student2❌,IStudent);接口约束可以有多个
值类型 --- 结构类型struct、int、double、bool、枚举
引用类型 -- 数组、类、接口、委托、object、字符串
① int i = new int();//怀疑int也是自身的包装类
② show<T>(T t) where T:class,new()这种写法也是可以的,同一个参数有多重约束(属于无参构造方法的类)
③ new() 约束一定要写在最后面
④ 多个泛型类型支持写对各个泛型约束(Show(T,S,K,D)(T t)
where D:IStudent
where K:Student
where S:struct
where T:class,new())
⑤ 对于类而言,泛型约束:class Student<T> : Student2 where T:new()
static void Main(string[] arg){
Student student = new Student();
IStudent student1 = null;
Show(student1); ❌ //只能接收入参是(带无参数的构造函数)的类
Show(student);✔
}
//约束--表示T类型只接收带一个无参数的构造函数(where T:new() 或 where T:Student)
public static void Show<T>(T t) where T:new()
{
Console.WriteLine(t);
}
interface IStudent<T>(){//泛型接口
}
//类的泛型约束
class Student<T> : Student2 where T:new() //普通类
{
//默认有一个无参数的构造
}
class Student2 //普通类
{
//默认有一个无参数的构造
}
1.6. 协变和逆变
逆变(父类转子类)、协变(子类转父类)
List<People> peoples =new List<People>();
① 其实从现实 中理解 他应该正确才对,但是List<People>类型和 List<Teacher>不是一个类型,从类型的角度来不成立,所以其实这个是语法规则不支持。.net2.0没有,3.0的时候才有协变和逆变
② 协变和逆变它是针对泛型接口和泛型委托来说的,离开了它们就没有这个说法
③ out关键字代表是协变(子->父),in代表是逆变(父->子)
④ 什么情况下使用?在知道自己或别人以后有用到父类通过子类实例化,或子类通过父类实例化的情况下可以用out 或in 关键字
//List<People> peoples1 = new List<Teacher>():
IListOut<People> listOut = new ListOut<People>();IListOut<People>listOut1 = new ListOut<Teacher>();//协变
IListlN<Teacher> listiN = new ListlN<Teacher>();IListlN<Teacher> listiN1 = new ListIN<People>();//逆变
lOutintList<Teacher, People> myList1 = new OutlntList<Teacher, People>();lOutintList<Teacher, People> myList2 = new OutintList<Teacher, Teacher>();/协变lOutintList<Teacher, People> myList3 = new OutintList<People, People>();//逆变l0utintList<Teacher, People> myList4 = new OutlntList<People, Teacher>();//逆变(父类转子类)+协变(子类转父类)
///<summary>
/// 协变只能返回结果,不能做参数(派生类转父类!)
///</summary>
///<typeparam name="T"></typeparam>
interface IListOut<out T>
{
T GetT();
}
class ListOut<T>:IListOut<T>
{
public T GetT(){
return default(T);//default关键字,如果是值类型则默认返回0,引用类型默认返回null
}
}
///<summary>
/// in修饰,逆变后,T只能作为当参数 不能做返回值,
/// </summary>
/// <typeparam name="T"></typeparam>interface lListIN<in T>
{
// GetT();
void show(T t);
}
class ListIN<T> : IListIN<T>{
public void Show(T t){
}
}
public interface lOutIntList<in inT, out outT>
{
void show(inT t);
outT Get();
outT Do(inT t);
/// out 只能是返回值 in只能是参数/// void Show1(outT t)
///inT Get1();
}
//逆变(父类转子类)+协变(子类转父类)
//协变和逆变感觉用处不大,有点诡异。。。T1从People变成了Teacher?不知道有何意义
public class OutintList<T1, T2>: lOutintList<T1,T2>{
public void Show(T1 t)//逆变作为参数
{ }
public T2 Get()//协变作为返回值
{return default(T2);}
public T2 Do(T1 t)
{return default(T2);}}
2.1. 反射 Reflection
类似用来操作metadata元数据的工具
DLL/EXE文件内部包含元数据(metadata) 和中间语言(IL)
① metadata(元数据) 是DLL/exe的一个数据清单(类、方法、特性、属性字段)
② 当前项目/bin/Debug/xx.interface.pdb是编译调试用的文件
③ exe / dll (主要区别就是exe文件有main方法,可执行)
④ 通过JIT去读取元数据metadata,编译成机器码文件;另外还有反射的方式也可以读取元数据
用途 》》:
【1】更新程序时(更新自己的DLL)
【2】使用别人的DLL文件(这种可以读取别人的私有的东西)
反射是什么? 它就是一个操作metadata的一个类库(可以把反射当成一个小工具用来读取或者操作元数据的)
类、方法、特性、属性字段。(为什么要通过反射间接去操作,1--因为我们需要动态,2--读取私有的对象)
那些地方使用到了: asp.net MVC ----ORM---LOC---AOP 几乎所有裤架都会使用反射!!!
///</summary>
2.1.1. 反射使用说明
三种方式加载:1.dll文件名、2.完整路径、3.完全限定名
① 每个项目都会生成一个dll文件(当前项目/bin/Debug/Interface.dll文件)
② Assembly 可以用来转载 dll文件
③一个项目生成一个dll文件,内部包含多个cs类文件,Assembly.GetTypes()相当于找全部类
namespace Ant.DB.SQLServer
{
public class SQLServerHelper : lDBHelper
{public SQLServerHelper()
{
Console.WriteLine(“{0}被构造”,this.GetType().Name);
}
public void Query(){
Console.WriteLine("{0}.Query", this.GetType0.Name);}
}
}
Console.WriteLine("----------------Reflection--------------");
Assembly assembly= Assembly.Load("Ant.DB.SQLServer”);//加载方式一:dll文件名
foreach (var type in assembly.GetTypes())//找所有类型(所有类){
Console.WriteLine(type.Name);
foreach (var method in type.GetMethods())//类型下面的所有方法
{
Console.WriteLine("这是"+method.Name+"方法”);
构造方法要用特殊的方法找
}
}
Assembly assembly = Assembly.Loadfile(@"C:\Users\Administrator\Desktop\奇艺教程\C#语法进阶\MyReflection\Ant.DB.MySqIbin\Debug\Ant.DB.Interface.dll”);//加载方式二:完整路径
因为是接口的DLL所以不包含object自带的方法(Equals\GetHashCode\GetType\ToString)
Assembly assembly = Assembly.LoadFrom("Ant.DB.SQLServer.dll");//方法三:完全限定名
2.1.2. 使用反射插件对象(Activator.CreateInstance(Type))
第一种加载方式,需要dll文件名在当前目录里面;
try{
Console.WriteLine("*********UseReflection 使用反射创建对象*******");
Assembly assembly = Assembly.LoadFrom("Ant.DB.SQLServer.dll”);//【1]加载DLL文件
Type type = assembly.GetType(”Ant.DB.sQLServer.SQLServerHelper”);//【2】获取类型(要完整类型名称)
object oDbHelper = Activator.Createlnstance(type);//创建对象
//SQLServerHelper sqlHelper = new SQLServerHelper();
//转成接口的话,方便后期应用于多个数据库API调用
IDBHelper dBHelper = oDbHelper as IDBHelper;//类型转换(as转换不报错,类型不对就返回null)
//IDBHelper dBHelper2 =(lDBHelper)oDbHelper
dBHelper.Query();
} catch(Exception ex){
Console.WriteLine(ex.Message);
}
2.1.3. 反射创建对象(带参数的构造函数)
Console.WriteLine("******cotr&Parameter********");
Assembly assembly= Assembly.LoadFrom("Ant.DB.SQLServer.dll”);
Type type = assembly.GetType(Ant.DB.SQLServer.ReflectionTest");
//获取到这个类型下面所有构造方法
foreach(Constructorlnfo ctor in type.GetConstructors()))//获取到所有的构造 方法{
Console.WriteLine(ctor.Name);
foreach(var parameter in ctor.GetParameters()//获取到构造方法的所有参数类型
{
Console.WriteLine(parameter.ParameterType);//显示类型名称
}
}
Console.WriteLine("************创建对象************");
object oCotr1 = Activator.Createlnstance(type);//无参数构造函数object oCotr2 = Activator.CreateInstance(type, new object[] {"Ant编程"});
object oCotr3 = Activator.Createlnstance(type, new object[] { 123});
object oCotr3 = Activator.Createlnstance(type, new object[] { 123,"ant编程"});
class ReflectionTest {
public ReflectionTest(){
Console.WriteLine($"这是{this.GetType()}无参数构造函数");
}
public ReflectionTest(string name){Console.WriteLine($“这是{this.GetType()}有参数构造函数,类型为{name.GetType()}”);
}
public ReflectionTest(int id){
Console.WriteLine($”这是{this.GetType0}有参数构造函数,类型为{id.GetType0}”);
}
public ReflectionTest(int id,string name){Console.WriteLine($“这是{this.GetType(}有2个参数构造函数,类型为{id.GetType0}和 {name.GetType0}”);
}
}
2.1.4. 反射创建对象(调用私有构造函数)
Activator.CreateInstance(Type type(反射得到的类信息), boolean nonPublic(true:创建私有的构造函数));//别人不想让你创建对象,用了私有,可以反射绕开
①也可以用反射去破坏单例模式,从而创建多个对象
namespace Ant.DB.SQLServer
{public class PrivateCtor{