【详细】【转】C#中理解委托和事件 事件的本质其实就是委托 RabbitMQ英汉互翼(一),RabbitMQ, RabbitMQ教程, RabbitMQ入门
【详细】【转】C#中理解委托和事件
文章是很基础,但很实用,看了这篇文章,让我一下回到了2016年刚刚学委托的时候,故转之!
1.委托
委托类似于C++中的函数指针(一个指向内存位置的指针)。委托是C#中类型安全的,可以订阅一个或多个具有相同签名方法的函数指针。简单理解,委托是一种可以把函数当做参数传递的类型。很多情况下,某个函数需要动态地去调用某一类函数,这时候我们就在参数列表放一个委托当做函数的占位符。在某些场景下,使用委托来调用方法能达到减少代码量,实现某种功能的用途。
1.1.自定义委托
声明和执行一个自定义委托,大致可以通过如下步骤完成:
- 利用关键字delegate声明一个委托类型,它必须具有和你想要传递的方法具有相同的参数和返回值类型;
- 创建委托对象,并且将你想要传递的方法作为参数传递给委托对象;
- 通过上面创建的委托对象来实现该委托绑定方法的调用。
下面一段代码,完成了一次应用委托的演示:


- //step01:使用delegate关键字声明委托
- public delegate int CalcSumDelegate(int a, int b);
- class Program
- {
- static void Main(string[] args)
- {
- //step03:实例化这个委托,并引用方法
- CalcSumDelegate del = new CalcSumDelegate(CalcSum);
- //step04:调用委托
- int result = del(5, 5);
- Console.WriteLine("5+5=" + result);
- }
- //step02:声明一个方法和委托类型对应
- public static int CalcSum(int a, int b)
- {
- return a + b;
- }
- }


通过上面4个步骤,完成了委托从声明到调用的过程。接着,咱也学着大神用ILSpy反编译上面的代码生成的程序集。截图如下:
- 自定义委托继承关系是:System.MulticastDelegate —> System.Delegate —>System.Object。
- 委托类型自动生成3个方法:BeginInvoke、EndInvoke、Invoke。查资料得知,委托正是通过这3个方法在内部实现调用的。Invoke 方法,允许委托同步调用。上面调用委托的代码del(5, 5)执行时,编译器会自动调用 del.Invoke(5,5);BeginInvoke 方法,允许委托的异步调用。假如上述委托以异步的方式执行,则要显示调用dal.BeginInvoke(5,5)。
注意:BeginInvoke 和 EndInvoke 是.Net中使用异步方式调用同步方法的两个重要方法,具体用法详见微软官方示例。
1.2.多播委托
一个委托可以引用多个方法,包含多个方法的委托就叫多播委托。下面通过一个示例来了解什么是多播委托:


- //step01:声明委托类型
- public delegate void PrintDelegate();
- public class Program
- {
- public static void Main(string[] args)
- {
- //step03:实例化委托,并绑定第1个方法
- PrintDelegate del = Func1;
- //绑定第2个方法
- del += Func2;
- //绑定第3个方法
- del += Func3;
- //step04:调用委托
- del(); //控制台输出结果:
- //调用第1个方法!
- //调用第2个方法!
- //调用第3个方法!
- }
- //step02:声明和委托对应签名的3个方法
- public static void Func1()
- {
- Console.WriteLine("调用第1个方法!");
- }
- public static void Func2()
- {
- Console.WriteLine("调用第2个方法!");
- }
- public static void Func3()
- {
- Console.WriteLine("调用第3个方法!");
- }
- }


可以看出,多播委托的声明过程是和自定义委托一样的,可以理解为,多播委托就是自定义委托在实例化时通过 “+=” 符号多绑定了两个方法。
Q:为什么能给委托绑定多个方法呢?
自定义委托的基类就是多播委托MulticastDelegate ,这就要看看微软是如何对System.MulticastDelegate定义的:
MulticastDelegate拥有一个带有链接的委托列表,该列表称为调用列表,它包含一个或多个元素。在调用多路广播委托时,将按照调用列表中的委托出现的顺序来同步调用这些委托。如果在该列表的执行过程中发生错误,则会引发异常。(--摘自MSDN)
Q:为什么使用“+=”号就能实现绑定呢?
先来看上述程序集反编译后的调用委托的代码:

“+=”的本质是调用了Delegate.Combine方法,该方法将两个委托连接在一起,并返回合并后的委托对象。
Q:多播委托能引用多个具有返回值的方法吗?
答案是,当然能。委托的方法可以是无返回值的,也可以是有返回值的。不过,对于有返回值的方法需要我们从委托列表上手动调用。否则,就只能得到委托调用的最后一个方法的结果。下面通过两段代码验证下:
正确做法:利用GetInvocationList获得委托列表上所有方法,循环依次执行委托,并处理委托返回值。
1.3.匿名方法
匿名方法是C#2.0版本引入的一个新特性,用来简化委托的声明。假如委托引用的方法只使用一次,那么就没有必要声明这个方法,这时用匿名方法表示即可。


- //step01:定义委托类型
- public delegate string ProStrDelegate(string str);
- public class Program
- {
- public static void Main(string[] args)
- {
- //step02:将匿名方法指定给委托对象
- ProStrDelegate del = delegate(string str) { return str.ToUpper(); };
- string result = del("KaSlFkaDhkjHe");
- Console.WriteLine(result);
- Console.ReadKey();
- //输出:KASLFKAFHKJHE
- }
- }


匿名方法只是C#提供的一个语法糖,方便开发人员使用。在性能上与命名方法几乎无异。
匿名方法通常在下面情况下使用:
- 委托需要指定一个临时方法,该方法使用次数极少;
- 这个方法的代码很短,甚至可能比方法声明都短的情况下使用。
1.4.Lambda表达式
Lambda表达式是C#3.0版本引入的一个新特性,它提供了完成和匿名方法相同目标的更加简洁的格式。下面示例用Lambda表达式简化上述匿名方法的例子:


- public delegate string ProStrDelegate(string str);
- public class Program
- {
- public static void Main(string[] args)
- {
- //匿名委托
- //ProStrDelegate del = delegate(string str) { return str.ToUpper(); };
- //简化1
- //ProStrDelegate del1 = (string str) =>{ return str.ToUpper(); };
- //简化2
- //ProStrDelegate del2 = (str) =>{ return str.ToUpper(); };
- //简化3
- ProStrDelegate del3 = str => str.ToUpper();
- string result = del3("KaSlFkaDhkjHe");
- Console.WriteLine(result);
- Console.ReadKey();
- //输出:KASLFKAFHKJHE
- }
- }


- 简化1:去掉delegate关键字,用"=>"符号表示参数列表和方法体之间的关系;
- 简化2:去掉方法的参数类型;假如只有一个参数,参数列表小括号()也可省略;
- 简化3:如果方法体中的代码块只有一行,可以去掉 return,去掉方法体的大括号{}。
1.5.内置委托
上述几种委托的使用,都没能离开定义委托类型这一步骤。微软干脆直接把定义委托这一步骤封装好,形成三个泛型类:Action<T>、Func<T>和Predicate<T>,这样就省去了定义的步骤,推荐使用。


- public class Program
- {
- public static void Main(string[] args)
- {
- //Action
- Action<string> action = delegate(string str) { Console.WriteLine("你好!" + str); };
- action("GG");
- //Func
- Func<int, int, int> func = delegate(int x, int y) { return x + y; };
- Console.WriteLine("计算结果:" + func(5, 6));
- //Predicate
- Predicate<bool> per = delegate(bool isTrue) { return isTrue == true; };
- Console.WriteLine(per(true));
- }
- }


它们的区别如下:
- Action<T>委托:允许封装的方法有多个参数,不能有返回值;
- Func<T>委托:允许封装的方法有多个参数,必须有返回值;
- Predicate<T>委托:允许封装的方法有一个参数,返回值必须为bool类型。
2.事件
委托是一种类型,事件依赖于委托,故事件可以理解为是委托的一种特殊实例。它和普通的委托实例有什么区别呢?委托可以在任意位置定义和调用,但是事件只能定义在类的内部,只允许在当前类中调用。所以说,事件是一种类型安全的委托。
2.1.定义事件
通过一个简单的场景来演示下事件的使用:


- /// <summary>
- /// 音乐播放器
- /// </summary>
- public class MusicPlayer
- {
- //step01:定义 音乐播放结束 事件
- public event EventHandler<EventArgs> PlayOverEvent;
- public string Name { get; set; }
- public MusicPlayer(string name)
- {
- this.Name = name;
- }
- //step02:定义一个触发事件的方法
- public void PlaySong()
- {
- //模拟播放
- Console.WriteLine("正在播放歌曲:" + this.Name);
- for (int i = 0; i < 20; i++)
- {
- Console.Write(".");
- Thread.Sleep(100);
- }
- //播放结束,则触发PlayOverEvent事件
- if (PlayOverEvent != null)
- {
- PlayOverEvent(this, null);
- }
- }
- }
- public class Program
- {
- static void Main(string[] args)
- {
- //创建音乐播放器对象
- MusicPlayer player = new MusicPlayer("自由飞翔");
- //step03:注册事件
- player.PlayOverEvent += player_PlayOverEvent;
- //播放歌曲,结束后触发事件
- player.PlaySong();
- Console.ReadKey();
- }
- static void player_PlayOverEvent(object sender,EventArgs e)
- {
- MusicPlayer player = sender as MusicPlayer;
- Console.WriteLine("\r\n{0}播完了!", player.Name);
- }
- }


程序运行结果:
总结上面事件使用的几个步骤:
- 用event关键字定义事件,事件必须要依赖一个委托类型;
- 在类内部定义触发事件的方法;
- 在类外部注册事件并引发事件。
public event EventHandler<EventArgs> PlayOverEvent
这句代码在MusicPlayer类定义了一个事件成员PlayOverEvent,我们说事件依赖于委托、是委托的特殊实例,所以EventHandler<EventArgs>肯定是一个委托类型。下面我们来验证一下:
EventHandler是微软封装好的事件委托,该委托没有返回值类型,两个参数:sender事件源一般指的是事件所在类的实例;TEventArgs事件参数,如果有需要创建,要显示继承System.EventArgs。
2.2.事件的本质
- MusicPlayer player = new MusicPlayer("自由飞翔");
- //注册事件
- player.PlayOverEvent += player_PlayOverEvent;
- player.PlaySong();
从上面代码我们观察到,事件要通过"+="符号来注册。我们猜想,事件是不是像多播委托一样通过Delegate.Combine方法可以绑定多个方法?还是通过反编译工具查看下。
我们看到PlayOverEvent事件内部生成了两个方法:add_ PlayOverEvent和remove_ PlayOverEvent。add方法内部调用Delegate.Combine把事件处理方法绑定到委托列表;remove方法内部调用Delegate.Remove从委托列表上移除指定方法。其实,事件本质上就是一个多播委托。
3.参考文章
[1] Edison Chou,http://www.cnblogs.com/edisonchou/p/4827578.html
[2] jackson0714,http://www.cnblogs.com/jackson0714/p/5111347.html
[3] Sam Xiao, http://www.cnblogs.com/xcj26/p/3536082.html
【转:http://www.cnblogs.com/esofar/p/5493028.html】
RabbitMQ英汉互翼(一),RabbitMQ, RabbitMQ教程, RabbitMQ入门
RabbitMQ is a message broker: it accepts and forwards messages. You can think about it as a post office: when you put the mail that you want posting in a post box, you can be sure that Mr. Postman will eventually deliver the mail to your recipient. In this analogy, RabbitMQ is a post box, a post office and a postman.
RabbitMQ 是这样一个消息代理:它接收和转发消息。你可以把它想像成是一个邮局:当你把一份邮件投递到信箱时,你可以确信的是邮递员先生终究会把邮件递送给接收者。在这个比喻中,RabbitMQ 扮演了信箱、邮局以及邮递员这一系列角色。
The major difference between RabbitMQ and the post office is that it doesn't deal with paper, instead it accepts, stores and forwards binary blobs of data ‒ messages.
RabbitMQ与邮局最大的不同在于它并不处理纸质信件,取而代之的是它接收、储存以及转发二进制消息数据。
RabbitMQ, and messaging in general, uses some jargon.
RabbitMQ 以及消息传递,通常会使用到一些专业术语。
Producing means nothing more than sending. A program that sends messages is a producer :
生产者的含义无非就是发送,一个程序在发送消息时它就是生产者:
A queue is the name for a post box which lives inside RabbitMQ. Although messages flow through RabbitMQ and your applications, they can only be stored inside a queue. A queue is only bound by the host's memory & disk limits, it's essentially a large message buffer. Many producers can send messages that go to one queue, and many consumers can try to receive data from one queue. This is how we represent a queue:
队列实质上就是 RabbitMQ 内部的“信箱”,作为消息,尽管自 RabbitMQ 流经应用程序,但它最终只会存储于队列中。队列只会受限于主机内存和磁盘空间,本质上来讲它其实是一个庞大的消息缓存区。多个生产者可以发送消息到同一个队列,同时多个消费者也可以从同一个队列接收数据。下图是我们描绘的一个队列的模样:
Consuming has a similar meaning to receiving. A consumer is a program that mostly waits to receive messages:
同理,消费也有着类似接收的含义,消费者就是一个主要用来等待接收消息的程序:
Note that the producer, consumer, and broker do not have to reside on the same host; indeed in most applications they don't.
要注意的是,生产者、消费者,以及代理之间不必存在于同一台主机,事实上大部分应用程序也不会这么做。
"Hello World"
"起步"
In this part of the tutorial we'll write two programs in C#; a producer that sends a single message, and a consumer that receives messages and prints them out. We'll gloss over some of the detail in the .NET client API, concentrating on this very simple thing just to get started. It's a "Hello World" of messaging.
在教程的当前部分我们会编写两个 C# 程序。作为生产者会发送一条消息,同时消费者会接收消息并将其打印出来。我们会忽略 API 当中的一些细节,把精力集中在简单的事情上从而更好的起步。这是一个“Hello World”的消息。
In the diagram below, "P" is our producer and "C" is our consumer. The box in the middle is a queue - a message buffer that RabbitMQ keeps on behalf of the consumer.
在下图中,"P" 就是生产者,"C"就是消费者。中间的方形盒子就是队列,即 RabbitMQ 为消费者保留的消息缓冲区。
The .NET client library
.NET 客户端库
RabbitMQ speaks multiple protocols. This tutorial uses AMQP 0-9-1, which is an open, general-purpose protocol for messaging. There are a number of clients for RabbitMQ in many different languages. We'll use the .NET client provided by RabbitMQ.
RabbitMQ 支持多种协议,本教程使用的是 AMQP 0-9-1,它是公开的较为通用的消息协议。RabbitMQ 支持多种语言的客户端,在这里我们将使用 RabbitMQ 提供的 .NET 客户端。
The client supports .NET Core as well as .NET Framework 4.5.1+. This tutorial will use RabbitMQ .NET client 5.0 and .NET Core so you will ensure you have it installed and in your PATH.
RabbitMQ 提供的 .NET 客户端已支持 .NET Core 以及 .NET Framework 4.5.1+,本教程将会使用 RabbitMQ .NET client 5.0 和 .NET Core,所以你需要确认已安装成功。
You can also use the .NET Framework to complete this tutorial however the setup steps will be different.
你也可以使用 NET Framework 来完成本教程,然而其安装步骤会有所不同。
RabbitMQ .NET client 5.0 and later versions are distributed via nuget.
RabbitMQ .NET client 5.0 以及最新的版本是经由 Nuget 来发布的。
This tutorial assumes you are using powershell on Windows. On MacOS and Linux nearly any shell will work.
本教程假定你在 Windows 操作系统中会使用 PowerShell,可以放心的是,在 MacOS 和 Linux 中几乎所有的 Shell 环境都能正常运行。
Setup
安装
First lets verify that you have .NET Core toolchain in PATH:
首先让我们验证一下本地环境变量 PATH 中的 .NET Core 工具链:
dotnet --help
should produce a help message.
这时应当产生一个帮助消息。
Now let's generate two projects, one for the publisher and one for the consumer:
现在让我们来创建两个项目,一个是发布者,一个是消费者。
dotnet new console --name Send
mv Send/Program.cs Send/Send.cs
dotnet new console --name Receive
mv Receive/Program.cs Receive/Receive.cs
This will create two new directories named Send and Receive.
这一步将会创建两个文件夹,一个叫 Send,一个叫 Receive。
Then we add the client dependency.
紧接着我们来添加客户端依赖。
cd Send
dotnet add package RabbitMQ.Client
dotnet restore
cd ../Receive
dotnet add package RabbitMQ.Client
dotnet restore
Now we have the .NET project set up we can write some code.
好了,现在我们完成了 .NET 项目的安装,这样就可以写一些代码了。
Sending
发送
We'll call our message publisher (sender) Send.cs and our message consumer (receiver) Receive.cs. The publisher will connect to RabbitMQ, send a single message, then exit.
随后我们会调用消息发布者(发送)Send.cs,以及消息消费者(接收)Receive.cs。发布者会连接 RabbitMQ,并发送一条消息,然后退出。
In Send.cs, we need to use some namespaces:
在 Send.cs 中,我们需要引入一些命名空间:
using System;
using RabbitMQ.Client;
using System.Text;
Set up the class:
建立类文件:
class Send
{
public static void Main()
{
...
}
}
then we can create a connection to the server:
然后我们就可以创建一个通往服务器的连接。
class Send
{
public static void Main()
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
...
}
}
}
}
The connection abstracts the socket connection, and takes care of protocol version negotiation and authentication and so on for us. Here we connect to a broker on the local machine - hence the localhost. If we wanted to connect to a broker on a different machine we'd simply specify its name or IP address here.
该连接是抽象自套接字连接,为我们处理协议关于版本在协商与认证等方面的事宜。在这里,我们会连接到本地机器的一个代理,也就是 localhost。如果我们想连接到一台不同机器上的代理,只需简单地指定主机名或者 IP 地址。
Next we create a channel, which is where most of the API for getting things done resides.
接下来我们将创建一个信道,它在众多的 API 中负责着事项的正确处理。
To send, we must declare a queue for us to send to; then we can publish a message to the queue:
为了能顺利的发送,我们需要先定义一个队列,然后我们就可以发布消息了。
using System;
using RabbitMQ.Client;
using System.Text;
class Send
{
public static void Main()
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using(var connection = factory.CreateConnection())
using(var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "hello",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
string message = "Hello World!";
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "",
routingKey: "hello",
basicProperties: null,
body: body);
Console.WriteLine(" [x] Sent {0}", message);
}
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}
Declaring a queue is idempotent - it will only be created if it doesn't exist already. The message content is a byte array, so you can encode whatever you like there.
声明队列的行为是幂等性的 - 即只有当一个队列不存在时才能被创建。
When the code above finishes running, the channel and the connection will be disposed.
当上述代码完成时,信道和连接将会被释放。
Here's the whole Send.cs class.
Sending doesn't work!
发送运行失败
If this is your first time using RabbitMQ and you don't see the "Sent" message then you may be left scratching your head wondering what could be wrong. Maybe the broker was started without enough free disk space (by default it needs at least 50 MB free) and is therefore refusing to accept messages. Check the broker logfile to confirm and reduce the limit if necessary. The configuration file documentation will show you how to set disk_free_limit.
如果这是你第一次使用 RabbitMQ,并且你没有收到“Sent”这一消息,这时你可能会抓耳挠腮,想知道是什么导致了错误。在这种情况下,有很大可能是因为代理(broker)在启动时没有足够的可用磁盘空间(默认至少需要 50M ),因此拒绝接收消息。检查代理(broker)日志文件(logfile),并进行确认以减少限制,通过查看配置文件文档,可以知晓如何设置 disk_free_limit。
Receiving
接收
That's it for our publisher. Our consumer is pushed messages from RabbitMQ, so unlike the publisher which publishes a single message, we'll keep it running to listen for messages and print them out.
以上是我们的发布者。我们的消费者已开始从 RabbitMQ 上被推送了消息,与发送一条消息的发布者有所不同的是,我们会让消费者持续地监听消息并将其打印出来。
The code (in Receive.cs) has almost the same using statements as Send:
在 Receive.cs 类文件中,using 区域的声明代码与 Send.cs 类文件近乎相同:
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;
Setting up is the same as the publisher; we open a connection and a channel, and declare the queue from which we're going to consume. Note this matches up with the queue that send publishes to.
与发布者的设置一样,我们打开一个连接和信道,并且声明好队列以作好消费的准备。需要注意的是,该队列与发布者所发送消息的队列是相匹配的(即基于同一个队列)。
class Receive
{
public static void Main()
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "hello",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
...
}
}
}
}
Note that we declare the queue here, as well. Because we might start the consumer before the publisher, we want to make sure the queue exists before we try to consume messages from it.
可以看到我们在消费者这里又声明了一次队列(QueueDeclare),在实践中,我们很可能是先启动消费者,后启动发布者。所以重复的声明代码,是当我们尝试从队列中消费消息时,确保队列总是已存在的。
We're about to tell the server to deliver us the messages from the queue. Since it will push us messages asynchronously, we provide a callback. That is what EventingBasicConsumer.Received event handler does.
我们即将告知服务器从队列中向我们递送消息,因为该动作是基于异步的,所以我们需要提供回调,这便是 EventingBasicConsumer.Received 事件处理方法所要做的。
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;
class Receive
{
public static void Main()
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using(var connection = factory.CreateConnection())
using(var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "hello",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Console.WriteLine(" [x] Received {0}", message);
};
channel.BasicConsume(queue: "hello",
autoAck: true,
consumer: consumer);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}
}
Here's the whole Receive.cs class.
Putting It All Together
融合一起
Open two terminals.
打开两个终端。
Run the consumer:
运行消费者:
cd Receive
dotnet run
Then run the producer:
紧接着运行生产者:
cd Send
dotnet run
The consumer will print the message it gets from the publisher via RabbitMQ. The consumer will keep running, waiting for messages (Use Ctrl-C to stop it), so try running the publisher from another terminal.
消费者会经由 RabbitMQ 打印出那些来自发布者的消息。消费者会持续运行,以等待消息(使用 Ctrl-C 来终止运行),如此,我们可以尝试从其他终端来运行发布者(重复运行多个生产者实例程序)。
【详细】【转】C#中理解委托和事件 事件的本质其实就是委托 RabbitMQ英汉互翼(一),RabbitMQ, RabbitMQ教程, RabbitMQ入门的更多相关文章
- 【详细】【转】C#中理解委托和事件
文章是很基础,但很实用,看了这篇文章,让我一下回到了2016年刚刚学委托的时候,故转之! 1.委托 委托类似于C++中的函数指针(一个指向内存位置的指针).委托是C#中类型安全的,可以订阅一个或多个具 ...
- c#打包文件解压缩 C#中使用委托、接口、匿名方法、泛型委托实现加减乘除算法 一个简单例子理解C#的协变和逆变 对于过长字符串的大小比对
首先要引用一下类库:using Ionic.Zip;这个类库可以到网上下载. 下面对类库使用的封装方法: 得到指定的输入流的ZIP压缩流对象 /// <summary> /// 得到指定的 ...
- C#:委托和自定义事件
1. 委托概述 “委托”相当于C++中的“函数指针”,委托必须与所要“指向”的函数在“参数”和“返回类型”上保持一致; // 定义Person类 public class Person { publi ...
- 事件委托和JQ事件绑定总结
事件委托: 比喻:事件委托的事例在现实当中比比皆是.比如,有三个同事预计会在周一收到快递.为签收快递,有两种办法:一是三个人在公司门口等快递:二是委托给前台MM代为签收.现实当中,我们大都采用委托的方 ...
- 详细讲解nodejs中使用socket的私聊的方式
详细讲解nodejs中使用socket的私聊的方式 在上一次我使用nodejs+express+socketio+mysql搭建聊天室,这基本上就是从socket.io的官网上的一份教程式复制学习,然 ...
- 转:二十一、详细解析Java中抽象类和接口的区别
转:二十一.详细解析Java中抽象类和接口的区别 http://blog.csdn.net/liujun13579/article/details/7737670 在Java语言中, abstract ...
- ASP.NET中定制自己的委托和事件参数类
本文笔记选自<庖丁解牛:纵向切入ASP.NET 3.5控件和组件开发技术>一书 一般在实际开发中,对于事件不需要传递数据信息时,像上面的KingTextBox控件的事件,在引发事件时传递的 ...
- javascript事件委托和jQuery事件绑定on、off 和one
一. 事件委托什么是事件委托?用现实中的理解就是:有100 个学生同时在某天中午收到快递,但这100 个学生不可能同时站在学校门口等,那么都会委托门卫去收取,然后再逐个交给学生.而在jQuery 中, ...
- 理解的javascript自定义事件
理解的javascript自定义事件 被我拖延了将近一个月的javascript事件模型系列终于迎来了第四篇,也是我计划中的最后一篇,说来太惭愧了,本来计划一到两个星期写完的,谁知中间遇到了很多事情, ...
随机推荐
- putty中文乱码问题解决
###putty中文乱码问题解决 用putty从windows xp连接ubuntu server或者FreeBSD系统,其中中文部分乱码,经常遇到这个问题的时候,会觉得很郁闷.现共享一些解决这个问题 ...
- HTML form without CSRF protection,HTML表单没有CSRF保护
HTML form without CSRF protection =HTML表单没有CSRF保护 CSRF是伪造客户端请求的一种攻击,CSRF的英文全称是Cross Site Request For ...
- Windows Socket五种I/O模型——代码全攻略(转)
Winsock 的I/O操作: 1. 两种I/O模式 阻塞模式:执行I/O操作完成前会一直进行等待,不会将控制权交给程序.套接字 默认为阻塞模式.可以通过多线程技术进行处理. 非阻塞模式:执行I/O操 ...
- 华登区块狗系统APP开发
华登区块狗系统开发,陈翎:{.l8O..285l..l22O.}华登区块狗软件开发,华登区块狗APP开发,华登区块狗模式开发,华登区块狗现成源码,狗狗集市理财模式开发 华登区块狗是什么?华登区块狗ap ...
- SwiftyUserDefaults对NSUserDefaults的封装扩展
SwiftyUserDefaults 是对NSUserDefaults的一些封装和扩展,这个库这个只有一个类,操作起来十分简单方便: 这里只有两个步骤来使用SwiftyUserDefaults: st ...
- 洛谷——P3018 [USACO11MAR]树装饰Tree Decoration
P3018 [USACO11MAR]树装饰Tree Decoration 比较水的一道树上模拟水题,更新每个点的价值为以这个点为根的子树中的价值最小值,同时更新以每个节点为根的$sum$值,即以这个节 ...
- 这几道Java集合框架面试题在面试中几乎必问
Arraylist 与 LinkedList 异同 1. 是否保证线程安全: ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全: 2. 底层数据结构: Arraylis ...
- ubuntu环境搭建DNS服务器
1 安装bind9 apt install bind9 2 修改 named.conf.local,添加要解析的域名及对应的域名配置文件 zone "test.cn"{ type ...
- Linux命令rsync使用总结
详细用法见:https://www.cnblogs.com/oboth-zl/articles/10334754.html rsync命令简介 主要用于数据同步.备份和镜像,除了本地使用之外,也可以通 ...
- Go:函数、defer
一.函数可赋值给一个变量 示例1: package main import "fmt" func add(a, b int) int { return a + b } func m ...