Bootstrap

C#学习笔记之——委托、线程、事件、lamda表达式

1、委托——delegate\Action

1.1 delegate

委托可以理解为函数指针。
委托delegate关键词,Delegate和delegate都可以,这个类似于string和String,c#程序编写一般还是用delegate小写的。委托定义的变量和类定义的变量一样,也是一种类型的对象。

public delegate void Game();//定义一个委托,这是一个类型,注意区别类型和对象的区别,对象是类型的具体化可以这样理解,Game现在就是一个类型,可以用Game定义Game类型的对象,这个类型是无返回值无参数的委托类型
//声明一种委托对象的方式
//方式01
public Game game_01;//定义一个委托的实例对象,和定义变量一样,直接定义,这时候game_01值为null
//方式02
Game game_01=new Game(“方法名称”);//和类的声明不同,委托声明的时候括号内必须有值,也就是方法名称,错误提示为“Game不包含0个参数的构造函数”,类括号内无参数是因为类的构造函数没有参数

public void Play(Game game)
{
//委托的执行方式有
//方式01,直接加上括号,就执行了所指向的方法
game();
//方式02,
game.Invoke();//这种方式也有人在用,一般都是用上一种方法,这一种见到了知道是什么意思就行
}
//下面定义了几个游戏
public void WangZheRongYao()
{
Console.WriteLine("王者荣耀");
}
public void HePingJingYing()
{
Console.WriteLine("和平精英");
}
        static void Main(string[] args)
        {
        	Game game;
        	game=WangZheRongYao;
			Play(game);//Play需要传入的是委托类型
            Console.ReadKey();
            #region
            Game game_01=new Game(WangZheRongYao);
            Play(game_01);
            Console.ReadKey();
            #endregion
        }

可以这样理解:delegate这个关键词的使用和class关键词的使用类似。首先是这两个关键词在写法上是小写,其次在创建上也是非常相近,还有就是委托和类的等级一样,都是定义在命名空间,委托不是包含在类中,当然委托也可以定义在类中,委托类型是一种函数指针,就是等于函数名称,这个委托类型的变量保存的就是这个函数在内存中的地址。
另外在声明委托对象和类对象的时候有点不同,用new关键字后面的括号里面委托需要传入参数,类因为有无参的构造函数所以不要传入参数。

  1. 委托是引用类型,在没有被赋值时候为null;
  2. 因为委托存放的为函数地址,所以定义委托时候会有“返回值类型”和“括号”以及“括号内的形参”;
  3. 理解声明和定义,在创建类的时候总是说“定义一个类”,定义一个新的委托类型的时候会说“声明委托”
  4. 委托声明了以后就定了可以由这个委托引用的方法,这个委托就只能引用和他有相同标签的方法,也就是说和他有相同返回值和传入参数的方法;
  5. 一旦声明了委托类型,委托对象要用new关键字来创建,且与一个方法有关;
  6. 一旦声明了一个或者说定义了一个类对象,类对象就要用new关键字来创建,并调用构造函数,
  7. 其实类和委托一样
  8. 需要注意的是:委托对象创建的时候new关键字后面括号里面为函数名称,不带参数;

例如:

//我们在定义一种新“对象”类型的时候
//定义一个学生Student类
public class Student
{
}
//实例化一个student对象
Student student=new Student();//无参数,调用的是构造函数
//我们在定义一个新的委托类型的时候
//定义一个新“委托”类型的时候
//定义一个Delegate委托
public delegate void DD();
DD dd=new DD(function);//有参数,参数为function,必须有参数,因为委托没有构造方法
dd();//这就算执行了function函数了,就是在委托变量的后面加上括号,也可以dd.Invoke()

1.2 Action

    class Interpreter
    {
        public void SayHi(Action language)
        {
            language.Invoke();
        }
        public void Chinese()
        {
            Console.WriteLine("你好");
        }
        
    }

        static void Main(string[] args)
        {

            
            Interpreter interpreter = new Interpreter();
            Action language = interpreter.Chinese;
            interpreter.SayHi(language);

            Console.ReadKey();


        }
    public delegate void Language();//定义一个委托类型
    
    class Interpreter
    {

        public void SayHi(Language language)
        {
            //language();
            language.Invoke();
        }
        public void Chinese()
        {
            Console.WriteLine("你好");
        }
    }

    static void Main(string[] args)
        {
            Language language;
            
            Interpreter interpreter = new Interpreter();
            //下面的这个动作就可以理解为插卡动作
            language = interpreter.Chinese;//注意这里不能加括号,只是把函数名的地址给到委托
            if(language==null)
            {
                Console.WriteLine("没有检测的插卡!");
            }
            interpreter.SayHi(language);
            Console.ReadKey();


        }

从上面两段代码可以看出来,Action就是委托,只是对delegate的进一步封装,用action就不用先定义委托了。

2、线程——Thread/Task

线程=任务
Task是Thread的一个包装类,使用方法基本上和Thread一样

ParameterizedThreadStart ParStart = new ParameterizedThreadStart(ThreadMethod);
 
Thread myThread = new Thread(ParStart);
 
object o = "hello";
 
myThread.Start(o);
 
 
 
//ThreadMethod如下:
 
public void ThreadMethod(object ParObject)
 
{
 
    //程序代码
 
}

带参数的方法需要这样开多线程

Thread线程
Task任务,对线程的封装
Task.Run();
delegate委托
event delegate
Action对委托的封装
event Action()
时间委托
委托的广播和订阅,实现数据传递
this.Invoke(new Action(()=>{});委托给一个匿名函数
线程的取消
CancellationTokenSource

2.1 同步

同步的sync,界面会卡顿,

2.2 多线程

开启多个线程,多个线程会同时执行

2.3 异步多线程

异步多线程是像同步一样,执行上有一定的顺序,异步多线程有关键字await、async关键字

3、事件——event

关于委托事件event和多线程Thread,这两个会一起出现,这是一种设计模式的问题,委托事件的广播和订阅的概念,
如果一个进程中有多个线程,线程之间的数据是不能被调用和访问的,一个线程想要访问其他线程的数据就要用的委托,用到委托就有了委托事件,就有了委托事件的广播和订阅的概念。
委托事件的定义和委托一样,就是在关键词Action前面加上event关键字
委托事件的订阅是“+=”,vs可以自动生成订阅事件的函数

class TaoBao//定义了一个淘宝类型,
{
List<string> list =new List<string>
{
"淘宝产品-01",
"淘宝产品-02",
"淘宝产品-03",
"淘宝产品-04",
"淘宝产品-05",
"淘宝产品-06",
"淘宝产品-07",
"淘宝产品-08",
"淘宝产品-09",
"淘宝产品-10",
}
public event Action<string> SetDatas;//这里定义了一个委托事件,函数名为SetDatas,带参数,参数为string类型

//其他方法
public void CollectDatas()
{
	foreach(string item in list)//list是一个列表数据,这里是取出每个元素
	{
	SetDatas(item);//把采集到的数据通过一个委托方法发送到别的线程,这里是主窗体的UI线程,
	//调用这个方法就是把数据广播出去,谁订阅就能得到这个数据,进而处理这个数据
	Thread.Sleep(1000);
}
}
//这里写订阅
private void btnStart_Click(object sender,EventArgs e)
{
	//实例化一个淘宝类
	TaoBao taobao =new TaoBao();
	taobao.SetDatas+=Taobao_SetDatas;//这里就是订阅事件,这里vs可以自动给你定义一个委托方法,方法的名称
	//方法的名称和定义都自动可以生成,这里只是订阅了事件,但是现在这个实例化对象还是空值null,里面并没有数据
	 Thread t1=new Thread(taobao.CollectDatas);//创建一个线程,传入的参数是一个委托类型,也就是一个函数名称
	 t1.Start();//开启线程任务
	
}
private void Taobao_SetDatas(string obj)
{
//上面的obj就是广播过来的字符串string格式的数据,现在通过订阅到这个广播,就能拿到这个数据
//拿到这个数据后就可以把它赋值给窗体上的控件,
this.lbDatas.Invoke(new Action(()=>{//关于句柄,窗体上的控件都有一个句柄id作为标识,这里就是找到这个控件所在的
//线程,让后把数据给到这个线程,然后显示的
//注意:其实这里执行的代码是窗体线程的,
	this.lbDatas.Items.Add(obj);
}));
}

4、lamda表达式

运算符 “=>”意思是:go to
(参数)=>{函数体}
意思是参数传递到函数体内,可以这样的理解。

;