# 常用设置

# VS快捷键

CTRL+K+D:快速对齐代码
CTRL+K+C:注释所选代码
CTRL+K+U:撤销注释代码
CTRL+J:弹出智能提示
SHIFT+HOME SHIFT+END

# 格式转换

convert(null)=0 parse(null) 报错

# 命名方式

类/方法:大驼峰 属性/变量:小驼峰

# 可空类型修饰符(?)

int? a = null

# 空合并运算符 (??)

a ?? b 当a为null时则返回b,a不为null时则返回a本身

# 等同符(===)

当两边值的类型相同时,直接比较值,若类型不相同,直接返回false

# NULL检查运算符(?.)

如果对象为NULL,则不进行后面的获取成员的运算,直接返回NULL
string name=Person?.Name

# DateTime? 赋值






 
 

//主要用到向下兼容原理,DateTime?继承于DateTime;
string req = "为字符串的参数";
DateTime? dt = null;
DateTime? time = string.IsNullOrEmpty(req) ? dt : Convert.ToDateTime(req);

ViewBag.courseDateTime = 
CourseDateTime.HasValue ? CourseDateTime.Value.ToString("HH:MM:ss") : "";

# String和string区别

  • String是一个类,string是一种数据类型.
  • string是c#中的类,String是.net Framework的类(在c# IDE中不会显示蓝色)
  • c# string映射为.net Framework的String
  • 如果用string,编译器会把它编译成String,所以如果直接用String就可以让编译器少做一点点工作
  • 如果使用c#,建议使用string,比较符合规范
  • string始终代表 System.String(1.x) 或 ::System.String(2.0) ,String只有在前面有using System;的时候并且当前命名空间中没有名为String的类型(class、struct、delegate、enum)的时候才代表 System.String
  • string是关键字,String不是,也就是说string不能作为类、结构、枚举、字段、变量、方法、属性的名称,而String可以

# 数组和集合

  • int[ ]、string[ ]:固定长度,固定类型
  • Array:固定长度,任意类型
  • ArrayList:任意长度,任意类型
  • List< T >:ArrayList的泛型版本,需要设置好类型
  • HashTable:哈希表实现了IDictionary接口,集合中的值都是以键值对的形式存在
  • Dictionary< T,T >:HashTable的泛型版本,需要设置好类型
  • HashSet< T >:对两个集合求交集、并集、差集等
  • Queue:队列是一种先进先出的结构,即元素从队列尾部插入,从队列的头部移除
  • Stack:栈是一种先进后出的结构,即元素从栈的尾部插入,从栈的尾部移除
  • SortedList:称为有序列表,按照 key 值对集合中的元素排序

提示

定义空数组:string[] names = { } string[] names = default

# 比较数组是否相同

using System;
using System.Linq;
...
var a1 = new int[] { 1, 2, 3};
var a2 = new int[] { 1, 2, 3};
var a3 = new int[] { 1, 2, 4};
var x = a1.SequenceEqual(a2); // true
var y = a1.SequenceEqual(a3); // false

# 两个list合并

List<int> listA = new List<int> {1,2,3,5,7,9};
List<int> listB = new List<int> {13,4,17,29,2};

List<int> Result = listA.Union(listB).ToList<int>(); //剔除重复项
List<int> Result = listA.Concat(listB).ToList<int>(); //保留重复项

# do while 循环

先执行 do{ } 中语句块的内容,再判断 while() 中布尔表达式的值是否为 True,如果为 True,则继续执行语句块中的内容,否则不执行,因此 do while 语句中的语句块至少会执行一次。

do
{
    语句块;
}
while(布尔表达式);

break 用于中断循环不再执行
continue 会跳过当前循环中的代码,强制开始下一次循环

# 枚举

  • 枚举使用enum关键字来声明,与类同级
  • 枚举本身的修饰符仅能使用public和internal
  • 枚举成员不能相同,但枚举的值可以相同

# 声明枚举

  • 不给成员赋值,成员的常数值默认从0开始
  • 给成员赋值,下一个成员的值为上一个成员值+1
//不给成员赋值,成员的常数值默认从0开始
public enum Week
{
	Sunday,
	Monday,
	Tuesday,
	Wednesday,
	Thursday,
	Friday,
	Saturday
}
//给成员赋值,下一个成员的值为上一个成员值+1
public enum Number
{
	One = 1,
	Two,
	Three,
	Seven = 7,
	Eight,
	Nine
}

# 类和对象

# 类的定义

类定义的具体语法形式如下:

类的访问修饰符    修饰符    类名
{
    类的成员
}

# 类的访问修饰符

用于设定对类的访问限制

  • private:仅能被当前类中的代码访问,如果类没有使用任何访问修饰符,则默认为private
  • protected:只能由当前类或派生类中的代码访问
  • private protected:新增的,访问限于当前类或当前类的派生类
  • internal:仅能被同一个项目中的代码访问
  • public:可以被任何代码访问

# 修饰符

修饰符是对类本身特点的描述

  • abstract 是抽象的意思,使用它修饰的类不能被实例化
  • sealed 修饰的类是密封类,使用它修饰的类不能被继承
  • static 修饰的类是静态类,使用它修饰的类不能被不能被继承、实例化

# 类名

类名用于描述类的功能,在同一个命名空间下类名必须是唯一的。

# 类的成员

在类中能定义的元素,主要包括字段、属性、方法。

# 定义字段

定义字段的语法形式如下:

//访问修饰符    修饰符    数据类型    字段名;
//在这里访问修饰符和修饰符都是可以省略的
class Test
{
	private int id;                         //定义私有的整型字段 id
	public readonly string name;            //定义公有的只读字符串类型字段 name
	internal static int age;                //定义内部的静态的整型字段 age
	private const string major = "计算机";  //定义私有的字符串类型常量 major
}

修饰符:readonly (只读)、static (静态的))、const (常量)、virtual(虚的)
使用 readonly 修饰字段意味着只能读取该字段的值而不能给字段赋值
使用 static 修饰的字段是静态字段,可以直接通过类名访问该字段

# 定义属性

定义属性的语法形式如下:

访问修饰符    修饰符    数据类型  属性名
{
    get
    {
        获取属性的语句块;
        return 值;
    }
    set
    {
        设置属性得到语句块;
    }
}

在给字段赋值时直接将 value 值赋给字段,如果要对赋给字段的值加以限制,可以先判断 value 值是否满足条件,如果满足条件则赋值,否则给字段赋默认值或进行其他操作

public double Price
{
    get
    {
        return price;
    }
    set
    {
		 if(value >= 0)
		 {
			 price = value;
		 }
		 else
		 {
			 price = 0;
		 }
    }
}

自动属性设置

public int Id{get; set;}
public string Name{get; set;}
public double Price{get; set;}

# 定义方法

定义方法的语法形式如下:

访问修饰符    修饰符    返回值类型    方法名(参数列表)
{
    语句块;
}

修饰符:virtual(虚拟)、abstract(抽象)、override(重写)、static(静态)、sealed(密封)

# 静态类

# 静态类

  • 仅包含静态成员
  • 静态类的本质,是一个抽象的密封类,所以不能被继承,也不能被实例化
  • 不能包含实例构造函数
  • 如果一个类下面的所有成员,都需要被共享,那么可以把这个类定义为静态类
  • 非静态类可以包含静态的字段、属性或方法

# 静态成员

  • 在类第一次加载的时候,这个类下面的所有静态成员会被创建一次。一旦创建直到程序退出,才被回收
  • 在静态方法中,不能直接调用实例成员,因为静态方法被调用的时候,对象还有可能不存在
  • this/base 关键字在静态方法中不能使用,因为有可能对象还不存在

# 静态构造函数

  • 静态类可以有静态构造函数,一个类只能有一个静态构造函数
  • 静态构造函数可以用于静态类,也可用于非静态类
  • 静态构造函数无访问修饰符、无参数,只有一个 static 标志
  • 静态构造函数不可被调用,当引用任何静态成员之前,静态构造函数被自动执行,并且只执行一次
  • 无参数的构造函数可以与静态构造函数共存。尽管参数列表相同,但一个属于类,一个属于实例
static class test   
{   
	/// <summary>   
	/// 静态构造函数/类构造函数   
	/// </summary>   
	static test()   
	{   
	}   
}  

静态构造函数

# 抽象类

  • abstract修饰的类叫做抽象类,抽象类不能实例化
  • 抽象方法只能在抽象类中,如果类成员被abstract修饰,则该类必须为抽象类
  • 如果子类没有实现抽象基类中所有的抽象方法,则子类也必须定义成一个抽象类
  • abstract修饰的方法不能有实现体,相当于纯虚函数
public abstract Light {
    public  void  turnon()
    {
        ...
    }
    public abstract  void  turnoff();
};

# 与Virtual的区别

  • 二者都是用来修饰父类的,只能作为基类使用,通过让子类与override的配合重新定义覆盖父类
  • 方法前面必须添加public,virtual或abstract就是让子类重新定义的,而private成员不能被子类访问
  • abstract可以修饰类和方法,virtual用于修饰方法和属性
  • virtual 修饰的方法可以在一般类中
  • 其修饰的方法在父类中一定要有实现,即使只是一对大括号
public  Light {
  public  virtual  void  turnon() {};
};

# 密封类

  • 用于第三方类库不想被客户端继承,或用于没有必要再继承的类,以防止滥用继承造成层次结构混乱
  • sealed用于类时,表示该类不能再被继承,不能和 abstract 同时使用,因为这两个修饰符互相排斥
  • 用于方法时,表示该方法或属性不能再被重写,必须和 override 关键字一起使用

# 构造函数初始化器

有时,在一个类中有几个构造函数,以容纳某些可选参数,这些构造函数都包含一些共同的代码

class Car {  
	private string description;   
	private uint nWheels;  
	public Car(string description, uint nWheels) 
	{         
		this.description = description;        
		this.nWheels = nWheels;     
	}   
	public Car(string description)   
	{        
		this.description = description;         
		this.nWheels = 4;    
	 } 
}

这两个构造函数初始化了相同的字段,显然,最好把所有的代码放在一个地方
C#构造函数初始化符可以包含对同一个类的另一个构造函数的调用,也可以包含对直接基类的构造函数的调用(使用相同的语法,但应使用base关键字代替this)
初始化符中不能有多个调用

class Car
 {  
      private string description;  
      private uint nWheels;  
      public Car(string description, uint nWheels)   
      {  
          this.description = description;  
          this.nWheels = nWheels;  
      }  
      public Car(string description) : this(description, 4)  
      {    }  
}

这里,this关键字仅调用参数最匹配的那个构造函数。注意,构造函数初始化器在构造函数之前执行
现在假定运行下面的代码: Car myCar = new Car("Proton Persona")
在本例中,在带一个参数的构造函数执行之前,先执行带2个参数的构造函数

# 析构函数

构造方法是在创建类的对象时执行的,而析构方法则是在垃圾回收、释放资源时使用的

~类名()
{
    语句块;
}

~User()
{
    Console.WriteLine("调用了析构方法");
}

# C#中的多态

基类的方法要加上关键字virtual后变成虚方法,才可以被重写
从而实现面向对象最重要的特征——多态性,即基类可以使用派生类的方法

public class Animal  
{  
   public virtual void Eat()  
   {  
      Console.WriteLine("Eat something");  
    }  
}

public class Cat : Animal  
{  
    public override void Eat()  
    {  //完全取代基类方法  
        Console.WriteLine("Eat small fishes!");  
    }  
}  
public class Dog : Animal  
{  
    public override void Eat()  
    {  //完全取代基类方法  
        Console.WriteLine("Eat small bones!");  
    }  
}

static void Main(string[] args)   
{  
    Animal mycat = new Cat();  
    Animal mydog = new Dog();  
    mycat.Eat();  
    mydog.Eat();  
}

# C#中的继承

  • 继承:指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系
  • 实现:指的是一个class类实现interface接口(可以是多个)的功能;实现是类与接口之间最常见的关系

当一个类A能够获取另一个类B中所有非私有的属性和方法作为自己的部分或全部成分时,就称这两个类之间具有继承关系
被继承的类B称为父类或超类、基类,继承了父类或超类的类A称为子类、派生类

//继承的语法格式
类的访问修饰符  class  派生类名:基类名
{
    类的成员
}

如果派生类中的成员变量和基类的成员变量名称相同访问的时候要加上关键字new
而访问基类的方法就要用到关键字base关键字

public class Person  
{  
    protected string name;  
    protected string ID;  
    public Person()  
    {  
        name = "zhouzhou";  
        ID = "130552199301152555";  
     }  
     public virtual void GetInfo()  
     {  
         Console.WriteLine("姓名:{0}", name);  
         Console.WriteLine("身份证号:{0}", ID);  
     }  
}

//声明student是person的子类  
public class Student : Person     
{  
   private string StudentNo = "123456";  
   //用base关键字调用基类的构造函数  
   public Student():base() 
   {  
        Console.WriteLine("我的名字叫周周。");  
    }  
    public override void GetInfo()  
    {  
		//用base关键字调用父类的方法  
         base.GetInfo();     
         Console.WriteLine("学号:{0}", StudentNo);  
    }  
}

# C#中的委托

  • 无论是实例方法还是静态方法,只要他们的参数、返回类型和定义的委托一样,可以把他们封装到委托
  • 委托本质是一个类,可以进行实例化,它定义了方法的类型,可以把方法当作参数来进行传递,也可以把委托当作参数来进行传递
  • 委托实例:把方法赋值给委托变量的时候就创建了委托实例
//把方法当作参数来进行传递
public delegate void GetMoney(string name);//定义委托  
class Example1  
{  
	public static void getCCBmoney(string name)  
	{  
		Console.WriteLine("去建行取钱!");  
	}  
	public static void getABCmoney(string name)  
	{  
		Console.WriteLine("去农行取钱!");  
	}  
	static void Main(string[] agrs)  
	{  
		//委托实例化,创建委托实例
		//或者可以写成GetMoney gm=new GetMoney(getABCmoney) 
		GetMoney gm=getABCmoney;
		//执行委托,Invoke可以省略不写
		gm("小红");  
		//gm.Invoke("小红");
	}
}

//把委托当作参数来进行传递
public delegate void mydelegate(string s); 
class Example2  
{  
	static void Main()
	{
		Example2 p = new Example2(); //实例化类
		//委托实例化
	    mydelegate my1 = new mydelegate(p.show2); 
	    p.show(my1, "abcdefg"); //类实例调用方法
	    Console.ReadLine();
	}
	//定义调用的方法,其中第一个参数为委托
	private void show(mydelegate my, string s)
	{
	    my(s);
	}
	//定义被调用的方法
	private void show2(string s) 
	{
	    Console.WriteLine(s);
	}
}

# 多播委托

  • 指在一个委托中注册多个方法,在注册方法时可以使用加号、减号运算符来实现添加或撤销方法
  • 返回值:如果多播委托的返回类型不是void,那么调用者从最后一个被调用的方法来接收返回值。前面的方法仍然会被调用,但是其返回值就被弃用了
  • lambda 表达式不能被移除,因为lambda表达式生成的是不同的方法
class Program
{
    //定义购买商品委托
    public delegate void OrderDelegate();
    static void Main(string[] args)
    {
        //实例化委托
        OrderDelegate orderDelegate = new OrderDelegate(Order.BuyFood);
        //向委托中注册方法
        orderDelegate += Order.BuyCake;
        orderDelegate += Order.BuyFlower;
		//撤销的方法
		orderDelegate -= Order.BuyFlower;
        //调用委托
        orderDelegate();
    }
}
class Order
{
    public static void BuyFood()
    {
        Console.WriteLine("购买快餐!");
    }
    public static void BuyCake()
    {
        Console.WriteLine("购买蛋糕!");
    }
    public static void BuyFlower()
    {
        Console.WriteLine("购买鲜花!");
    }
}

# 匿名委托(匿名方法)

在委托中通过定义代码块来实现委托的作用
delegate关键字声明,后面跟随方法的参数列表,然后在编写方法体

public delegate void MyDelegate(int num1);
static void Main(string[] args)
{
   MyDelegate mdl = delegate(int n) { Console.WriteLine(n + 100); };
   //简写的写法就是采用了拉姆达表达式来写的
  // MyDelegate mdl = (n)=>{ Console.WriteLine(n + 100); };
   
   mdl(121);//理解:mdl其实就是声明出来的一个方法
}
public static void Say()
{
    Console.WriteLine("wo hen piao liang !");
}

# 委托的发展历程

  • C# 1.0 中,您通过使用在代码中其他位置定义的方法显式初始化委托来创建委托的实例
  • C# 2.0 引入了匿名方法的概念,作为一种编写可在委托调用中执行的未命名内联语句块的方式
  • C# 3.0 引入了 Lambda 表达式,这种表达式与匿名方法的概念类似,但更具表现力并且更简练。这两个功能统称为“匿名函数”。通常,针对 .NET Framework 版本 3.5 及更高版本的应用程序应使用 Lambda 表达式
public class HistoryDelegate
{
	private delegate void Delegate1(string str_);
	private void OnDelegate1(string str1_)
	{
		Console.WriteLine($"OnDelegate1: {str1_}");
	}

	public void OnTest()
	{
		//C# 1.0
		Delegate1 d1 = new Delegate1(OnDelegate1);
		d1("d1");
		//C# 2.0
		Delegate1 d2 = delegate (string str) { Console.WriteLine($"{str}"); };
		d2("d2");
		//C# 3.0
		Delegate1 d3 = (x) => { Console.WriteLine($"{x}"); };
		d3("d3");
	}
}

# 泛型委托

# Func

Func< Result >,Func< T1,Result >第一个是无参数,但是有返回值
它有16种形式,只是参数个数不同

public static string SayHello()
{
  return "Hello";
}

static void Main(string[] args)
{
  Func<string> say = SayHello;
  Console.WriteLine(say());
  Console.ReadKey();
}

# Action

Action< T >的用法与Func几乎一样,调用方法也类似
Action< T >的委托函数都是没有返回值的,最多16个参数

private delegate string Say();
public static void SayHello(string str)
{
  Console.WriteLine(str);
}

static void Main(string[] args)
{
  Action<string> say = SayHello;
  say("abc");
  Console.ReadKey();
}

Func与Action都支持Lambda的形式调用

Func<string, string> say = m => m + m;
Console.WriteLine(say("abc"));//输出abcabc

使用Func与Action的意义:
在之前.NET版本中已经出现过的特定类型的委托,是无法放弃的,这两种泛型委托就是一个统一的标准,后面的开发中直接使用,不需要再去自定义

//ThreadStart 是一个委托
//Show 是一个方法
ThreadStart threadStart=new ThreadStart(Show);
//参数是一个ThreadStart类型的委托
Tread thread=new Thread(threadStart);

//NoReturnNoPara 是自定义的委托
NoReturnNoPara noReturnNoPara=new NReturnNoPara(show);
//会报错  原因是:类型不匹配,Thread需要ThreadStart类型的委托
Tread thread=new Thread(noReturnNoPara);

# Predicate

Predicate 是返回bool型的泛型委托
Predicate< int > 表示传入参数为int 返回bool的委托,Predicate有且只有一个参数,返回值固定为bool

public class TestPredicate
{
	private bool OnPredicate1(int index_)
	{
		if (index_ > 0)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	private bool TestPredicate1<T>(Predicate<T> predicate1, T index_)
	{
		return predicate1(index_);
	}
	public void OnTest()
	{
		Console.WriteLine(TestPredicate1(OnPredicate1, 0));
	}
}

# C#中的事件

事件是一种引用类型,实际上也是一种特殊的委托。

//事件定义的语法形式如下
访问修饰符  event  委托名  事件名 ;

class Program
{
    static void Main(string[] args)
    {
        //创建MyEvent类的实例
        MyEvent myEvent = new MyEvent();
        //实例化事件,使用委托指向处理方法
        myEvent.BuyEvent += new MyEvent.BuyDelegate(MyEvent.BuyFood);
        myEvent.BuyEvent += new MyEvent.BuyDelegate(MyEvent.BuyCake);
        myEvent.BuyEvent += new MyEvent.BuyDelegate(MyEvent.BuyFlower);
        //调用触发事件的方法
        myEvent.InvokeEvent();
    }
}
class MyEvent
{
    //定义委托
    public delegate void BuyDelegate();
    //定义事件
    public event BuyDelegate BuyEvent;
    //定义委托中使用的方法
    public static void BuyFood()
    {
        Console.WriteLine("购买快餐!");
    }
    public static void BuyCake()
    {
        Console.WriteLine("购买蛋糕!");
    }
    public static void BuyFlower()
    {
        Console.WriteLine("购买鲜花!");
    }
    //创建触发事件的方法
    public void InvokeEvent()
    {
        //触发事件,必须和事件是同名方法
        BuyEvent();
    }
}

注意

如果事件的定义和调用不在同一个类中,实例化的事件只能出现在+=或者-=操作符的左侧。
在上面的代码中,实例化事件的代码只能写成
myEvent.BuyEvent += new MyEvent.BuyDelegate(MyEvent.BuyFood)的形式,
而不能使用myEvent.BuyEvent = new MyEvent.BuyDelegate(MyEvent.BuyFood)的形式。

# C#中的反射

反射:在不修改程序原码的情况下,实现程序功能的动态调整。

# Typeof() 和 GetType()

作用相同,都返回当前对象的类型

  • typeof(x)中的x,必须是具体的类名、类型名称等,不可以是变量名称。

  • GetType()方法继承自Object,所以C#中任何对象都具有GetType()方法,它的作用和typeof()相同,返回Type类型的当前对象的类型。

    比如有这样一个变量i:
    Int32 i = new Int32();

    i.GetType()返回值是Int32的类型,但是无法使用typeof(i),因为i是一个变量,如果要使用typeof(),则只能:typeof(Int32),返回的同样是Int32的类型。

  • 关于跨程序集的反射

    • 如果使用typeof,编译能通过,则跨程序集的反射一定可以正常运行。typeof是支持强类型的。

      Type supType = typeof(BNameSpace.SubSpace.Class);
      

      如果当前程序集没有添加对EnterpriseServerBase.dll的引用,则编译会报错。

    • Type.GetType是非强类型的。只会在当前程序集中进行类型搜索。
      解决的办法是:首先添加对EnterpriseServerBase.dll的引用,加载目标程序集,然后再使用Assembly.GetType方法来获取类型。

      Assembly asmb = Assembly.LoadFrom("EnterpriseServerBase.dll");
      Type supType = asmb.GetType("EnterpriseServerBase.DataAccess.IDBAccesser");
      

# 反射的核心类:System.Type

这个类封装了关于对象的信息,获得了类型的Type对象后,就可根据Type提供的属性和方法获取此类型的一切信息(方法、字段、属性、事件、参数、构造函数等)。
获取Type对象一般来说有三种获取方法:

//使用Type类提供的静态方法GetType()
Type t = Type.GetType("System.IO.Stream");
txtOutput.Text = t.ToString();
注意到GetType方法接受字符串形式的类型名称。

//使用 typeof 操作符
//如果在页首写入了using System.IO; 也可以直接用 typeof(Stream);
Type t = typeof(System.IO.Stream);
这时的使用有点像泛型,Stream就好像一个类型参数一样,传递到typeof操作符中。

//通过类型实例获得Type对象
String name = "Jimmy Zhang";
Type t = name.GetType();
使用这种方法时应当注意,尽管我们是通过变量去获取Type对象。
但是Type对象不包含关于这个特定对象的信息,仍是保存对象的类型(String)的信息。

# 反射程序集

在System.Reflection命名空间下有一个Assembly类型,它代表了一个程序集并包含了关于程序集的信息。
在程序中加载程序集时,我们可以使用 Assembly类型提供的静态方法LoadFrom() 和 Load()。

Assembly asm = Assembly.LoadFrom("Demo.dll");
//或者
Assembly asm = Assembly.Load("Demo");

当使用LoadFrom()方法时提供的是程序集的文件名,当将一个程序集添加到项目引用中以后,可以直接写“文件名.dll”。
如果想加载一个不属于当前项目的程序集,则需要给出全路径,比如:

Assembly asm = Assembly.LoadFrom(@"C:\WINDOWS\Microsoft.NET\System.Web.dll");

使用Load()方法的时候,只用提供程序集名称即可,不需要后缀名。

如果想获得当前程序集,可以使用Assembly类型的静态方法 GetExecutingAssembly。
它返回包含当前执行的代码的程序集(也就是当前程序集)。

Assembly as = Assembly.GetExecutingAssembly();

在获得一个Type类型实例以后,我们还可以使用该实例的Assembly属性来获得其所在的程序集:

Type t = typeof(int)
Assembly asm = t.Assembly;

下面按照 程序集->模块->类型 三个层次来分析一个程序集,当然还可以继续递归类型内部的成员。
最后通过CreateInstance方法来动态创建了一个类型,这些都是反射经常被用来完成的功能。

[Serializable]
class SimpleClass
{
    private String _MyString;
    public SimpleClass(String mystring)
    {
        _MyString = mystring;
    }
 
    public override string ToString()
    {
        return _MyString;
    }
 
    static void Main(string[] args)
    {
        Console.WriteLine("简单程序集");
        Console.Read();
    }
}
 
public class AnalyseHelper
{
    /// <summary>
    /// 分析程序集
    /// </summary>
    public static void AnalyzeAssembly(Assembly assembly)
    {
        Console.WriteLine("程序集名字:" + assembly.FullName);
        Console.WriteLine("程序集位置:" + assembly.Location);
        Console.WriteLine("程序集是否在GAC中:" +
                    assembly.GlobalAssemblyCache.ToString());
        Console.WriteLine("包含程序集的模块名" +
            assembly.ManifestModule.Name);
        Console.WriteLine("运行程序集需要的CLR版本:" +
            assembly.ImageRuntimeVersion);
        Console.WriteLine("现在开始分析程序集中的模块");
		//assembly也可以直接获取所有类型调用.gettypes()
        Module[] modules = assembly.GetModules();
        foreach (Module module in modules)
        {
            AnalyzeModule(module);
        }
    }
 
    /// <summary>
    /// 分析模块
    /// </summary>
    public static void AnalyzeModule(Module module)
    {
        Console.WriteLine("模块名:" + module.Name);
        Console.WriteLine("模块的UUID:" + module.ModuleVersionId);
        Console.WriteLine("开始分析模块下的类型");
        Type[] types = module.GetTypes();
        foreach (Type type in types)
        {
            AnalyzeType(type);
        }
    }
 
    /// <summary>
    /// 分析类型
    /// </summary>
    public static void AnalyzeType(Type type)
    {
        Console.WriteLine("类型名字:" + type.Name);
        Console.WriteLine("类型的类别是:" + type.Attributes);
        if (type.BaseType != null)
            Console.WriteLine("类型的基类是:" + type.BaseType.Name);
        Console.WriteLine("类型的GUID是:" + type.GUID);
        //设置感兴趣的类型成员
        BindingFlags flags = (BindingFlags.NonPublic | BindingFlags.Public 
		                       | BindingFlags.Static | BindingFlags.Instance);
        //分析成员
        FieldInfo[] fields = type.GetFields(flags);
        if (fields.Length > 0)
        {
            //Console.WriteLine("开始分析类型的成员");
            foreach (FieldInfo field in fields)
            {
                // 分析成员
            }
        }
        //分析包含的方法
		// MethodInfo类派生于MethodBase抽象类,而MethodBase类继承了MemberInfo类
        MethodInfo[] methods = type.GetMethods(flags);
        if (methods.Length > 0)
        {
            //Console.WriteLine("开始分析类型的方法");
            foreach (MethodInfo method in methods)
            {
                // 分析方法
            }
        }
        //分析属性
        PropertyInfo[] properties = type.GetProperties(flags);
        if (properties.Length > 0)
        {
            //Console.WriteLine("开始分析类型的属性");
            foreach (PropertyInfo property in properties)
            {
                // 分析属性
            }
        }
    }
}
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
class Program
{
	static void Main(string[] args)
	{    
		//加载程序集
		Assembly assembly = Assembly.LoadFrom(@"..\..\..\bin\Debug\SimpleAssembly.exe");
		AnalyseHelper.AnalyzeAssembly(assembly);

		//创建一个程序集中的类型的对象
		Console.WriteLine("利用反射创建对象");
		string[] paras = { "测试一下反射效果" };
		object obj = assembly.CreateInstance(assembly.GetModules()[0].GetTypes()[0].ToString(), 
							  true, BindingFlags.CreateInstance, null, paras, null, null);
		Console.WriteLine(obj);
		Console.ReadKey();
	}
}
interface IRun {
	void Run();
}
class Person : IRun
{
	public void Run()
	{
		Console.WriteLine("走,去LOL啊!");
	}
}
class Car : IRun
{
	public void Run()
	{
		Console.WriteLine("呜...........");
	}
}

class Program
{
	static void Main(string[] args)
	{
		IRun e = new Person();
		e.Run();
		Console.ReadLine();
	}
}

# 实现反射方式创建对象

可以通过 Activator.CreateInstance(静态)和 Assembly.CreateInstance(非静态)来实现。
其中Assembly.CreateInstance 内部调用的仍是Activator.CreateInstance。
根据要动态创建的类型对象是否处于当前程序集之中。
可将反射创建对象分为:创建程序集内的类型对象与创建程序集外的类型对象。

  • 创建程序集内的类型对象
```
private static void ReflectionIRun1(string className)
{
	string classPath = String.Format("namespace.{0}", className);
	//参数 null ,指出所要创建类型对象位于当前程序集 
	var handler = Activator.CreateInstance(null, classPath);
	IRun e = (IRun)handler.Unwrap();
	Console.WriteLine(e.Run());
}
private static void ReflectionIRun2(string className)
{
	string classPath = String.Format("namespace.{0}", className);
	//typeof(IRun).Assembly 获取 IRun 类型所在的程序集
	object obj = typeof(IRun).Assembly.CreateInstance(null, classPath);
	IRun e = (IRun)obj;
	Console.WriteLine(e.Run());
}
```
```
public static void Main()
{
   //用Activator .CreateInstance创建函数实例,默认的不带参数的构造函数
   IObjcet obj=(IObjcet)Activator.CreateInstance(
	 System.Type.GetType("ActivatorCreateInstance.ClassExam,ActivatorExample"),null
   );
   //System.Type.GetType  命名空间.类名,程序集
   obj.printName();

   //调用ClassExam类的另一个带参数构造函数
   IObjcet obj2=(IObjcet)System.Activator.CreateInstance(
	 System.Type.GetType("ActivatorCreateInstance.ClassExam,ActivatorExample")
	 ,new string []{"seted new name"}
   );
   obj2.printName();
}
```
  • 创建程序集外的类型对象
private static void ReflectionBoss1()
{
    string classPath ="Lib.Boss";
    //"Lib" 参数指明要加载的程序集(即要创建的对象类型在哪个程序集中定义)
    var handler = Activator.CreateInstance("Lib", classPath);
    Boss b = handler.Unwrap() as Boss;
    Console.WriteLine(b.Talk());
}
private static void ReflectionBoss2()
{
    string classPath ="Lib.Boss";
    //Assembly.Load("Lib") 加载的程序集(即要创建的对象类型在哪个程序集中定义)
    var assembly = Assembly.Load("Lib");
    Boss b = (Boss)assembly.CreateInstance(classPath);
    Console.WriteLine(b.Talk());
}

如果A,B,C,D都与执行代码同一个程序集.则可以这样调用
System.Reflection.Assembly.GetExecutingAssembly().CreateInstance("命名空间.类名", false);
如:object o = System.Reflection.Assembly.GetExecutingAssembly().CreateInstance("MyNameSpace.A", false);

不同程序集的话.则要装载调用.如下:
System.Reflection.Assembly.Load("程序集名称").CreateInstance("命名空间.类名", false);
如:object o = System.Reflection.Assembly.Load("MyDll").CreateInstance("MyNameSpace.A", false);

# dynamic

  • var和dynamic
    var和dynamic完全是两个概念,根本不应该放在一起做比较。

    var实际上是编译期抛给我们的“语法糖”,一旦被编译,编译期会自动匹配var 变量的实际类型,并用实际类型来替换该变量的申明,这看上去就好像我们在编码的时候是用实际类型进行申明的。

    而dynamic被编译后,实际是一个object类型,只不过编译器会对dynamic类型进行特殊处理,让它在编译期间不进行任何的类型检查,而是将类型检查放到了运行期。

  • 类型转换

    dynamic d1 = 7;
    dynamic d2 = "a string";
    dynamic d3 = System.DateTime.Today;
    dynamic d4 = System.Diagnostics.Process.GetProcesses();
     
    //反之亦然,类型为dynamic的任何表达式也能够隐式转换为其他类型。
    int i = d1;
    string str = d2;
    DateTime dt = d3;
    System.Diagnostics.Process[] procs = d4;
    
  • dynamic 反射
    因为反射是运行时的类型操作,所以在编程时面临类型不确定的问题。

    private static void ReflectionBoss1()
    {
        string classPath ="Lib.Boss";
        var handler = Activator.CreateInstance("Lib", classPath);
        dynamic b = handler.Unwrap();
        Console.WriteLine(b.Talk());
    }
    private static void ReflectionBoss2()
    {
        string classPath ="Lib.Boss";
        var assembly = Assembly.Load("Lib");
        dynamic b = assembly.CreateInstance(classPath);
        Console.WriteLine(b.Talk());
    }
    

# C#中的多义关键字

# new

new 关键字可用作运算符、修饰符或约束。

  • new 运算符:用于创建对象和调用构造函数。
  • new 修饰符:在用作修饰符时,new 关键字可以隐藏从基类继承的成员。
public class Parent
{
	public virtual void Print()
	{
		WriteLine("Parent Print");
	}
	public virtual void View()
	{
	}
}
public class Child : Parent
{
	public new void Print()
	{
		WriteLine("Child Print");
	}
	public override void View()
	{
	}
}
  • new 约束:用于在泛型声明中约束用作类型的参数必须要有Public无参数构造函数。
static void Show<T>(T t) where T : new()
{
	WriteLine(t.GetType());
}

提示

如果指定了构造函数,则不再有默认的无参构造函数,如果需要无参构造函数,则需要自己来写。

# default

default有两种用法,一是返回类型的默认值,二是在switch中作为非选项值。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Console;
namespace KeyWordsDemo
{
    class DefaultDemo : IDemo
    {
        public void Run()
        {
            //default 1
            WriteLine(default(int));
            WriteLine(default(bool));
            WriteLine(default(DateTime));
            WriteLine(default(string));

            switch (default(string))
            {
                case "":
                    WriteLine("空字符串");
                    break;
                case null:
                    WriteLine("null");
                    break;
                //default 2
                default:
                    WriteLine("其他");
                    break;
            }
        }
    }
}

# out

out有两种用法,一是作为方法输出参数,从方法内部返回数据。
二是用在接口和委托的协变中,如案例中的interface IParent< out R >

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Console;

namespace KeyWordsDemo
{
    class OutDemo : IDemo
    {
        public void Run()
        {
            //out 1
            Computer(out int result);
            WriteLine(result);

            WriteLine("------------");
            IParent<object> p1 = new Child<object>();
            IParent<object> p2 = new Child<string>();
            WriteLine(p1.GetType());
            WriteLine(p2.GetType());
            p1.Print();
            p2.Print();
            p1 = p2;
            p1.Print();

        }
        //out 1
        public void Computer(out int result)
        {
            result = 10;
        }

        //out 2
        interface IParent<out R>
        {
            R Print();
        }
        class Child<R> : IParent<R>
        {
            public R Print()
            {
                var r = default(R);
                WriteLine($"{typeof(R).Name}");
                return r;
            }
        }
    }
}

# where

where 子句用于指定类型约束(泛型约束)

  • 接口约束
public class MyGenericClass<T> where T:IComparable { }
  • 基类约束:必须将指定的类作为基类(或者就是该类本身,可以是枚举Enum),并且要在所有其他约束之前。
class MyClassy<T, U> where T : class where U : struct {}
  • 构造函数约束:new() 参数都必须具有可访问的无参数(或默认)构造函数。
public class MyGenericClass <T> where T: IComparable, new() {}

# 关键字extern用法

用于声明在外部实现的方法。extern 修饰符的常见用法是在使用 Interop 服务调入非托管代码时与 DllImport 属性一起使用;在这种情况下,该方法还必须声明为 static
在该示例中,程序接收来自用户的字符串并将该字符串显示在消息框中。程序使用从 User32.dll 库导入的 MessageBox 方法

using System;  
using System.Runtime.InteropServices;  
class MainClass   
{  
  [DllImport("User32.dll")]  
  public static extern int MessageBox(int h, string m, string c, int type);  
  //hovertree.com
  static int Main()   
  {  
      string myString;   
      Console.Write("Enter your message: ");  
      myString = Console.ReadLine();  
      return MessageBox(0, myString, "My Message Box", 0);  
  }  
}

extern 关键字还可以定义外部程序集别名,使得可以从单个程序集中引用同一组件的不同版本

# Lambda表达式

简化了匿名委托的使用,让你让代码更加简洁,优雅。
运算符将表达式分为两部分,左边指定输入参数,右边是lambda的主体。

//委托  逛超市
delegate int GuangChaoshi(int a);
static void Main(string[] args)
{          
    //GuangChaoshi gwl = JieZhang;
	GuangChaoshi gwl = p => p + 10;
	Console.WriteLine(gwl(10) + "");   //打印20,表达式的应用
	Console.ReadKey();
}        

其实表达式(p => p + 10;)中的 p 就代表委托方法中的参数,而表达式符号右边的 p+10,就是委托方法中的返回结果。

# params关键字

params 可变参数, 主要的用处是在给函数传参数时函数的参数不固定的时候

  • params 关键字之后不允许任何其他参数,并且只允许一个 params 关键字。
  • 参数数组必须是一维数组。
  • 不允许将params修饰符与ref和out修饰符组合起来使用。
  • 与参数数组对应的实参可以是同一类型的数组名,也可以是与该数组的元素属于同一类型的变量。
UseParams(1, 2, 3);  //同一类型的变量
UseParams2(1, 'a', "test");  //同一类型的变量
int[] myarray = new int[3] {10,11,12};
UseParams(myarray);  //同一类型的数组名

# Partial的应用

Partial是局部类型的意思。允许我们将一个类、结构或接口分成几个部分,在几个不同的.cs文件中实现。

  • 局部类型只适用于类、接口、结构,不支持委托和枚举。
  • 同一个类型的各个部分必须都有修饰符 partial。
  • 使用局部类型时,一个类型的各个部分必须位于相同的命名空间中。
  • 一个类型的各个部分上的访问修饰符必须维持一致性(abstract,sealed,static)。
  • 在局部类型上的特性具有“累加”效应。
[Attribute1, Attribute2("Hello")]
partial class Class1{}

[Attribute3, Attribute2("Exit")]
partial class Class1{}

//相当于
[Attribute1, Attribute2("Hello"), Attribute3, Attribute2("Exit")]
class Class1 {}

# 延迟加载

推迟加载的时机,当真正使用的时候才加载。

# Lazy< T >

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Demo_Lazy
{
    //定义了一个Hero类型
    public class Hero
    {
        public string Name { get; set; }

        public string FullName { get; set; }

        private readonly Lazy<Skill> skill;

        public Skill objSkill
        {
            get { return skill.Value; }
        }

        public Hero(string name)
        {
            Name = name;
            FullName = "Super " + name;
            skill = new Lazy<Skill>(() => new Skill(Name));
        }
    }

    //定义一个Skill类型
    public class Skill
    {
        public string Name { get; set; }

        public int Power { get; set; }

        public Skill(string name)
        {
            Name = name;
            Power = name.Length;
        }
    }


    public class Program
    {
        public static void Main(string[] args)
        {
            Hero hero = new Hero("qi da sheng");
            //此时获取Hero的FullName时并不会去创建Skill的实例
            Console.WriteLine(hero.FullName);
            //真正用到Skill.Name时才会创建Skill的实例,从而实现了延迟加载效果
            Console.WriteLine(hero.objSkill.Power);

            Console.Read();
        }
    }
}

# 属性缓存

//定义了一个Hero类型
public class Hero
{
    public string Name{get;set;}
    
    public string FullName{get;set;}
    
    //public Skill objSkill;
    private Skill _skill;
    
    public Skill objSkill
    {
        get {return _skill??(new _skill(Name));}
    }
    
    public Hero(string name)
    {
        Name=name;
        FullName="Super "+name;
        //objSkill=new Skill(name);
    }
}

//定义一个Skill类型
public class Skill
{
    public string Name{get;set;}
    
    public int Power{get;set;}
    
    public Skill(string name)
    {
        Name=name;
        Power=name.Length;
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        Hero hero=new Hero("qi da sheng");
        //此时获取Hero的FullName时并不会去创建Skill的实例
        Console.WriteLine(hero.FullName);
        //真正用到Skill.Name时才会创建Skill的实例,从而实现了延迟加载效果?
        Console.WriteLine(hero.ObjSkill.Power);
    }
}

# Virtual

1、只能用于EF框架。 2、类是由Public修饰,不能是封闭类,也就是不能带有Sealded修饰符。

public class Score
{
	[Key]
	public int Id { get; set; }

	public int StudentScore { get; set; }//学生分数

	public int StudentID { get; set; }//学生ID

	public int CourseID { get; set; }//课程ID

	public virtual Student Student { get; set; }//virtual关键字修饰

	public virtual Course Course { get; set; }//virtual关键字修饰
}

如果您不想使用延迟加载,您可以关闭Lazy Loading,将LazyLoadingEnabled设为false。
如果导航属性没有标记为virtual,Lazy Loading也是不起作用的。

public StudentContext(): base("StudentContext")//指定连接字符串
{
    this.Configuration.LazyLoadingEnabled = false; //关闭延迟加载
}

# 扩展方法

为现有的类型添加一个方法,现有类型既可以是int,string等数据类型,也可以是自定义的数据类型。
扩展方法是一种特殊的静态方法,必须定义在非泛型的静态类中。

//必须是静态类才可以添加扩展方法
static class Program {
    /// <summary>
    /// 随机返回 true 或 false
    /// </summary>
    /// <param name="random">this参数自动指定到Random的实例</param>
    /// <returns></returns>
    public static bool NextBool(this Random random) {
        return random.NextDouble() > 0.5;
    }

    static void Main(string[] args) {
        //调用扩展方法
        Random rd = new Random();
        bool bl = rd.NextBool();

        Console.WriteLine(bl.ToString());
        Console.ReadKey();
    }
}

自定义的数据类型。

/// <summary>
/// 购物车类 (实现 IEnumerable<Product> 接口)
/// </summary>
public class ShoppingCart : IEnumerable<Product> {
    public List<Product> Products { get; set; }
    public IEnumerator<Product> GetEnumerator() {
        return Products.GetEnumerator();
    }
    IEnumerator IEnumerable.GetEnumerator() {
        return GetEnumerator();
    }
}

/// <summary>
/// 定义一个静态类,用于实现扩展方法(注意:扩展方法必须定义在静态类中)
/// </summary>
public static class MyExtensionMethods {
    /// <summary>
    /// 计算商品总价钱
    /// </summary>
    public static decimal TotalPrices(this IEnumerable<Product> productEnum) {
        decimal total = 0;
        foreach (Product prod in productEnum) {
            total += prod.Price;
        }
        return total;
    }
}

class Program {
    static void Main(string[] args) {
        // 创建并初始化ShoppingCart实例,注入IEnumerable<Product>
        IEnumerable<Product> products = new ShoppingCart {
            Products = new List<Product> { 
                new Product {Name = "Kayak", Price = 275}, 
                new Product {Name = "Lifejacket", Price = 48.95M}, 
                new Product {Name = "Soccer ball", Price = 19.50M}, 
                new Product {Name = "Corner flag", Price = 34.95M}
            }
        };
        // 创建并初始化一个普通的Product数组
        Product[] productArray = { 
            new Product {Name = "Kayak", Price = 275M}, 
            new Product {Name = "Lifejacket", Price = 48.95M}, 
            new Product {Name = "Soccer ball", Price = 19.50M}, 
            new Product {Name = "Corner flag", Price = 34.95M} 
        };

        // 取得商品总价钱:用接口的方式调用TotalPrices扩展方法。
        decimal cartTotal = products.TotalPrices();
        // 取得商品总价钱:用普通数组的方式调用TotalPrices扩展方法。
        decimal arrayTotal = productArray.TotalPrices();

        Console.WriteLine("Cart Total: {0:c}", cartTotal);
        Console.WriteLine("Array Total: {0:c}", arrayTotal);
        Console.ReadKey();
    }
}

# Object类

Object 类是 C# 语言中最原始、最重要的类,是所有类的基类
每个 C# 类都是它的子类,它实现了每个类都必须具有的基本方法
Object 是引用类型。如果将值类型转换为引用类型存在拆/装箱,影响性能,比使用泛型耗时

在 Object 类中提供了 4 个常用的方法,即 Equals、GetHashCode、GetType 以及 ToString 方法。

  • Equals():用于比较两个对象是否相等。
  • GetHashCode():方法返回当前对象的哈希代码,每个对象的哈希值都是固定的。

# 引用传递、值传递

# 值类型和引用类型

  • 值类型:直接存储数据的值,保存在内存中。
    C#预定义的简单类型,像int,float,bool,char都是值类型,另外enum(枚举),struct(结构)也是值类型。
  • 引用类型:存储对值的引用,实际上存储的就是一个内存的地址。
    string,数组,自定义的class类、接口、委托和封装就都是引用类型了.其中的string是比较特殊的引用类型。

# 引用传递外和值传递

C#函数的参数如果不加ref,out这样的修饰符显式申明参数是通过引用传递外,默认都是值传递。

  • 传递值类型参数

    • 通过值传递值类型
      class PassingValByVal
      {
          static void SquareIt(int x)
          {
              x *= x;
              System.Console.WriteLine("The value: {0}", x);  //25
          }
          static void Main()
          {
              int n = 5;
              System.Console.WriteLine("The value before: {0}", n);  //5
      
              SquareIt(n); 
              System.Console.WriteLine("The value after: {0}", n);  //5
          }
      }
      
    • 通过引用传递值类型
      class PassingValByRef
      {
          static void SquareIt(ref int x)
          {
              x *= x;
              System.Console.WriteLine("The value : {0}", x);//25
          }
          static void Main()
          {
              int n = 5;
              System.Console.WriteLine("The value before : {0}", n);//5
      
              SquareIt(ref n); 
              System.Console.WriteLine("The value after : {0}", n);//25
          }
      }
      
  • 传递引用类型参数

    • 通过值传递引用类型

      class PassingRefByVal 
      {
          static void Change(int[] pArray)
          {
              pArray[0] = 888; 
              pArray = new int[5] {-3, -1, -2, -3, -4};
              System.Console.WriteLine("the first element is: {0}", pArray[0]);//-3
          }
      
          static void Main() 
          {
              int[] arr = {1, 4, 5};
              System.Console.WriteLine("before the first element is: {0}", arr[0]);//1
      
              Change(arr);
              System.Console.WriteLine("after the first element is: {0}", arr[0]);//888
          }
      }
      

      在上个示例中,数组 arr 为引用类型,在未使用 ref 参数的情况下传递给方法。在此情况下,将向方法传递指向 arr 的引用的一个副本。输出显示方法有可能更改数组元素的内容,在这种情况下,从 1改为 888。但是,在 Change 方法内使用 new 运算符来分配新的内存部分,将使变量 pArray 引用新的数组。因此,这之后的任何更改都不会影响原始数组 arr(它是在 Main 内创建的)。实际上,本示例中创建了两个数组,一个在 Main 内,一个在 Change 方法内。

    • 通过引用传递引用类型

      class PassingRefByRef 
      {
          static void Change(ref int[] pArray)
          {
              pArray[0] = 888;
              pArray = new int[5] {-3, -1, -2, -3, -4};
              System.Console.WriteLine("the first element is: {0}", pArray[0]);//-3
          }
              
          static void Main() 
          {
              int[] arr = {1, 4, 5};
              System.Console.WriteLine("before the first element is: {0}", arr[0]);//1
      
              Change(ref arr);
              System.Console.WriteLine("after the first element is: {0}", arr[0]);//-3
          }
      }
      

      方法内发生的所有更改都影响 Main 中的原始数组。实际上,使用 new 运算符对原始数组进行了重新分配。因此,调用 Change 方法后,对 arr 的任何引用都将指向 Change 方法中创建的五个元素的数组。

# out和ref之间的区别

  • 两者都是按引用传递的,使用后都将改变原来参数的数值。
  • ref可以把参数的数值传递进函数,但是out是要把参数清空,就是说你无法把一个数值从out传递进去的,out进去后,参数的数值为空。这个就是两个的区别,或者说就像有的网友说的,ref是有进有出,out是只出不进。
  • 传递到 ref 参数的参数必须最先初始化。这与 out 不同,后者的参数在传递之前不需要显式初始化。
class RefExample
{
    static void Method(ref int i)
    {
        i = 44;
    }

    static void Main()
    {
        int val = 0;
        Method(ref val);
        // val is now 44
    }
}
  • 如果一个方法采用 ref 或 out 参数,而另一个方法不采用这两类参数,则可以进行重载。
class RefOutOverloadExample
{
    public void SampleMethod(int i) { }
    public void SampleMethod(out int i) { }
}

# 特性(Attributes)

自定义特性,定义的方式和普通的类定义方式一致,但是,

  • 第一:需要继承标准特性类。
  • 第二:需要添加标准特性,用来限制特性的使用范围等。
  • 第三:必须要定义构造函数,即使是空的。
//基于标准特性
[AttributeUsage(AttributeTargets.All,AllowMultiple = true, Inherited=true)]
//继承标准特性类
class CustomAttr:Attribute                                                   
{
    //0参数构造函数
    public CustomAttr()
    {
 
    }
}

使用特性例子如下:(这里相当于实例化了2个类)

[CustomAttr(name ="dj",sore=100,message ="222")]
[CustomAttr(name = "sq", sore = 100, message = "666")]
class Test
{
 
}

提示

[CustomAttr(name ="dj",sore=100,message ="222")]
这里相当于调用了不带任何参数的构造函数,但是使用属性功能进行赋值
[CustomAttr("dj,100,"666")]
这里相当于直接调用了带有三个参数的构造函数

# C#中的异步

在 C# 7.0 之前,异步方法的返回值三种:Task,Task< T >,void。
在 C# 7.0 之后,除了上面三种还可以返回 ValueTask 和 ValueTask< T >。

Task 是一个引用类型,这就意味着每次调用异步方法都会在托管堆上生成一个 Task 实例,
如果瞬间调用 1w 次,那么托管堆上也瞬间存在 1w 个 Task 实例。
ValueTask 是值类型,所以避免了在 托管堆 上的内存分配,便拥有了更高的性能。

如果你的异步方法可以直接获取结果,那么建议将 Task< T > 改成 ValueTask< T >,
从而避免不必要的性能开销,Task 和 ValueTask 都是表示 可等待 的操作。

public Task<int> GetCustomerIdAsync()
{
    return Task.FromResult(1);
}

public ValueTask<int> GetCustomerIdAsync()
{
    return new ValueTask(1);
}

Task.FromResult主要用于以同步的方式实现一个异步接口。

interface IMyInterface
{
    Task<int> DoSthAsync();
}

public class MyClass : IMyInterface
{
    public Task<int> DoSthAsync()
    {
        int result = 1;
        return Task.FromResult(result);
    }
}

提示

如果使用Task.FromResult不带返回值,就使用Task.FromResult(0) 或 Task.FromResult(null)。

# 一句Task.Result就死锁

代码非常简单,把程序跑起来,点一下 click,果然界面卡住了,有点不可思议。

public partial class Form1 : Form
{
	public Form1()
	{
		InitializeComponent();
	}

	private void button1_Click(object sender, EventArgs e)
	{
		var jsonTask = GetJsonAsync("http://cnblogs.com").Result;
		textBox1.Text = jsonTask;
	}

	public async static Task<string> GetJsonAsync(string uri)
	{
		using (var client = new HttpClient())
		{
			var jsonString = await client.GetStringAsync(uri);
			return jsonString;
		}
	}
}

原因:task需要主线程来执行它,主线程却在傻傻的等待 task 的 complete 状态,所以延续的task永远得不到执行,这就出现了很尴尬的场面。

这破解之法:

  • 禁止将 延续task 丢到 Queue 中 要切断这条路,言外之意就是让线程池自己结束这个 task,这样 UI线程 就能感知到这个task已完成,最终 UI线程 就能获取最后的 html,做法就是在 await 后加上 ConfigureAwait(false)。




 




public async static Task<string> GetJsonAsync(string uri)
{
	using (var client = new HttpClient())
	{
		var jsonString = await client.GetStringAsync(uri).ConfigureAwait(false);
		return jsonString;
	}
}
  • 禁止阻塞主线程 如果不阻塞主线程,那么主线程就可以自由的在 Control.Queue 中获取需要执行的任务,改法也很简单,只需要在 GetJsonAsync 前加上 await 即可。
 

 



private async void button1_Click(object sender, EventArgs e)
{
	var jsonTask =await GetJsonAsync("http://cnblogs.com").Result;
	textBox1.Text = jsonTask;
}

# Socket

# TCP/IP

TCP/IP 即传输控制协议/网间协议,定义了主机如何连入因特网及数据如何再它们之间传输的标准。
TCP/IP协议参考模型把所有的TCP/IP系列协议归类到四个抽象层中:

  • 应用层:TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等等
  • 传输层:TCP,UDP
  • 网络层:IP,ICMP,OSPF,EIGRP,IGMP
  • 数据链路层:SLIP,CSLIP,PPP,MTU

socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用。

# 三次握手

  • 第一次握手:客户端尝试连接服务器,向服务器发送syn包(同步序列编号),syn=j,客户端进入SYN_SEND状态等待服务器确认。
  • 第二次握手:服务器接收客户端syn包并确认(ack=j+1),同时向客户端发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态。
  • 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

三次握手

# WebService

WebService请求类型都为Post,WebService的Url为:[WebServiceUrl]/[WebMethod]

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消注释以下行。 
[System.Web.Script.Services.ScriptService]

// 不是必要,但推荐添加(如果Person里面再嵌套另一个复杂类型,则必要声明)
[GenerateScriptType(typeof(Person))]        
public class WebService1 : System.Web.Services.WebService
{
	/// <summary>
	/// 无任何参数
	/// </summary>
	/// <returns></returns>
	[WebMethod]
	public string HelloWorld()
	{
		return "Hello World";
	}

	/// <summary>
	/// 传入参数
	/// </summary>
	/// <param name="name"></param>
	/// <returns></returns>
	[WebMethod]
	public string Hello(string name)
	{
		return string.Format("Hello {0}", name);
	}

	/// <summary>
	/// 返回泛型列表
	/// </summary>
	/// <param name="i"></param>
	/// <returns></returns>
	[WebMethod]
	public List<int> CreateArray(int i)
	{
		List<int> list = new List<int>();
		while (i >= 0)
		{
			list.Add(i--);
		}

		return list;
	}

	/// <summary>
	/// 返回复杂类型
	/// </summary>
	/// <param name="name"></param>
	/// <param name="age"></param>
	/// <returns></returns>
	[WebMethod]
	public Person GetPerson(string name, int age)
	{
		return new Person()
		{
			Name = name,
			Age = age
		};
	}
}

/// <summary>
/// 复杂类型
/// </summary>
public class Person
{
	public string Name { get; set; }
	public int Age { get; set; }
}

编写js调用以上方法

 //调用无参数方法
$("#btnHelloWorld").click(function(){
	$.ajax({
		type: "POST",
		contentType:"application/json",
		url:"WebService1.asmx/HelloWorld",
		data:"{}",
		dataType:'json',
		success:function(result){                   
			alert(result.d);
		}
	});
});       

//传入1个参数
$("#btnHello").click(function(){
	$.ajax({
		type: "POST",
		contentType:"application/json",
		url:"WebService1.asmx/Hello",
		data:"{name:'KiMoGiGi'}",
		dataType:'json',
		success:function(result){                   
			alert(result.d);
		}
	});
});

 //返回泛型列表
$("#btnArray").click(function(){
	$.ajax({
		type: "POST",
		contentType:"application/json",
		url:"WebService1.asmx/CreateArray",
		data:"{i:10}",
		dataType:'json',
		success:function(result){                   
			alert(result.d.join(" | "));
		}
	});
});

 //返回复杂类型
$("#btnPerson").click(function(){
	$.ajax({
		type: "POST",
		contentType:"application/json",
		url:"WebService1.asmx/GetPerson",
		data:"{name:'KiMoGiGi',age:26}",
		dataType:'json',
		success:function(result){
			var person = result.d;
			var showText = [];
			for(var p in person){
				showText.push(p + ":" + person[p]);
			}
			alert(showText.join("\r\n"));
		}
	});
});

webservice设置可跨域访问

<system.web>
   <webServices>
      <protocols>
        <add name="HttpGet"/>
        <add name="HttpPost"/>
        <add name="HttpSoap"/>
        <add name="Documentation"/>
      </protocols>
    </webServices>
  </system.web>
  <system.webServer>
    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Methods" value="OPTIONS,POST,GET"/>
        <add name="Access-Control-Allow-Headers" value="x-requested-with,content-type"/>
        <add name="Access-Control-Allow-Origin" value="*"/>
      </customHeaders>
    </httpProtocol>
  </system.webServer>
</system.web>

webservice无参的方式接收数据

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public void  hell()
{

	Stream s = HttpContext.Current.Request.InputStream;//从输入流获得json字符流
	//还原数据流
	byte[] b = new byte[s.Length];
	s.Position = 0;
	s.Read(b, 0, (int)s.Length);
	jsontext = Encoding.UTF8.GetString(b); //JSON字符串
}