什么时候在 C # 中使用委托?

您在 C # 中如何使用委托?

42251 次浏览

subscribing eventhandlers to events

Now that we have lambda expressions and anonymous methods in C#, I use delegates much more. In C# 1, where you always had to have a separate method to implement the logic, using a delegate often didn't make sense. These days I use delegates for:

  • Event handlers (for GUI and more)
  • Starting threads
  • Callbacks (e.g. for async APIs)
  • LINQ and similar (List.Find etc)
  • Anywhere else where I want to effectively apply "template" code with some specialized logic inside (where the delegate provides the specialization)
  1. For event handler

  2. To pass method in a method parameters

The first line of usage is to replace the Observer/Observable (events) pattern. The second, a nice elegant version of the Strategy pattern. Various other usages can be gathered, though more esoteric than these first two I think.

Events, other anynch operations

Any time you want to encapsulate behavior, but invoke it in a uniform way. Event Handlers, call-back functions, etc. You can accomplish similar things using Interfaces and casts, but sometimes, behavior isn't necessarily tied to a type or object. Sometimes you just have behavior you need to encapsulate.

Lazy parameter initialization! Besides all previous answers (strategy pattern, observer pattern, etc), delegates allow you to handle lazy initialization of parameters. For example, suppose you have a function Download() which takes quite a lot of time and returns a certain DownloadedObject. This object is consumed by a Storage depending on a certain Conditions. Typically, you would:

storage.Store(conditions, Download(item))

However, with delegates (more precisely, lambdas) you can do the following, by changing the signature of store so that it receives a Condition and a Func<Item,DownloadedObject> and use it like this:

storage.Store(conditions, (item) => Download(item))

Therefore, storage will only evaluate the delegate if necessary, executing download depending on Conditions.

A slightly different use is to speed up reflection; i.e. instead of using reflection each time, you can use Delegate.CreateDelegate to create a (typed) delegate to a method (a MethodInfo), and call that delegate instead. This is then much quicker per call, as the checks have already been done.

With Expression, you can also do the same to create code on the fly - for example, you can easily create an Expression that represents the + operator for a type chosen at runtime (to provide operator support for generics, which the language doesn't provide); and you can compile an Expression to a typed delegate - job done.

You can use delegates to declare function-typed variables and parameters.

Example

Consider the "resource borrowing" pattern. You want to control the creation and cleanup of a resource, while allowing client code to "borrow" the resource in between.

This declares a delegate type.

public delegate void DataReaderUser( System.Data.IDataReader dataReader );

Any method matching this signature can be used to instantiate a delegate of this type. In C# 2.0, this can be done implicitly, simply by using method's name, as well as by using anonymous methods.

This method uses the type as a parameter. Note the delegate's invocation.

public class DataProvider
{
protected string _connectionString;


public DataProvider( string psConnectionString )
{
_connectionString = psConnectionString;
}


public void UseReader( string psSELECT, DataReaderUser readerUser )
{
using ( SqlConnection connection = new SqlConnection( _connectionString ) )
try
{
SqlCommand command = new SqlCommand( psSELECT, connection );
connection.Open();
SqlDataReader reader = command.ExecuteReader();


while ( reader.Read() )
readerUser( reader );  // the delegate is invoked
}
catch ( System.Exception ex )
{
// handle exception
throw ex;
}
}
}

The function can be called with an anonymous method as follows. Note that the anonymous method can use variables declared outside of itself. This is extremely handy (although the example is a little contrived).

string sTableName = "test";
string sQuery = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='" + sTableName + "'";


DataProvider.UseReader( sQuery,
delegate( System.Data.IDataReader reader )
{
Console.WriteLine( sTableName + "." + reader[0] );
} );

Usage of delegates

  1. Event Handling
  2. Multi Casting

The comparison param in In Array.Sort(T[] array, Comparison comparison), List.Sort(Comparison comparison), etc

Found another interesting answer:

A coworker just asked me this question - what's the point of delegates in .NET? My answer was very short and one that he had not found online: to delay execution of a method.

Source: LosTechies

Just like LINQ is doing.

I use delegates to communicate with threads.

For example, I might have a win forms app which downloads a file. The app starts a worker thread to do the download (which prevents the GUI from locking up). The worker thread uses delegates to send status messages (eg download progress) back to the main program, so that the GUI can update the status bar.

Delegates are very useful for many purposes.

One such purpose is to use them for filtering sequences of data. In this instance you would use a predicate delegate which accepts one argument and returns true or false depending on the implementation of the delegate itself.

Here is a silly example - I am sure you can extrapolate something more useful out of this:

using System;
using System.Linq;
using System.Collections.Generic;


class Program
{
static void Main()
{
List<String> names = new List<String>
{
"Nicole Hare",
"Michael Hare",
"Joe Hare",
"Sammy Hare",
"George Washington",
};


// Here I am passing "inMyFamily" to the "Where" extension method
// on my List<String>.  The C# compiler automatically creates
// a delegate instance for me.
IEnumerable<String> myFamily = names.Where(inMyFamily);


foreach (String name in myFamily)
Console.WriteLine(name);
}


static Boolean inMyFamily(String name)
{
return name.EndsWith("Hare");
}
}

Delegates are used any time you use events - that's the mechanism by which they work.

In addition, delegates are very useful for things such as using LINQ queries. For example, many LINQ queries take a delegate (often Func<T,TResult>) which can be used for filtering.

Delegates can often be used in place of an interface with one method, a common example of this would be the observer pattern. In other languages if you want to receive a notification that something has happened you might define something like:

class IObserver{ void Notify(...); }

In C# this is more commonly expressed using events, where the handler is a delegate, for example:

myObject.SomeEvent += delegate{ Console.WriteLine("..."); };

Another great place to use delegates if when you have to pass a predicate into a function, for example when selecting a set of items from a list:

myList.Where(i => i > 10);

The above is an example of the lambda syntax, which could also have been written as follows:

myList.Where(delegate(int i){ return i > 10; });

Another place where it can be useful to use delegates is to register factory functions, for example:

myFactory.RegisterFactory(Widgets.Foo, () => new FooWidget());
var widget = myFactory.BuildWidget(Widgets.Foo);

I hope this helps!

As far as I know, delegates can be converted to function pointers. This makes life MUCH easier when interoperating with native code that takes function pointers, as they can effectively be object-orientated, even though the original programmer didn't make any provision for that to happen.

Delegate's are used to call a method by it's reference. For example:

  delegate void del_(int no1,int no2);
class Math
{
public static void add(int x,int y)
{
Console.WriteLine(x+y);
}
public static void sub(int x,int y)
{
Console.WriteLine(x-y);
}
}






class Program
{
static void Main(string[] args)
{
del_ d1 = new del_(Math.add);
d1(10, 20);
del_ d2 = new del_(Math.sub);
d2(20, 10);
Console.ReadKey();
}
}

I'm coming in on this really late but I was having trouble figuring out the purpose of delegates today and wrote two simple programs that give the same output that I think explains their purpose well.

NoDelegates.cs

using System;


public class Test {
public const int MAX_VALUE = 255;
public const int MIN_VALUE = 10;


public static void checkInt(int a) {
Console.Write("checkInt result of {0}: ", a);
if (a < MAX_VALUE && a > MIN_VALUE)
Console.WriteLine("max and min value is valid");
else
Console.WriteLine("max and min value is not valid");
}


public static void checkMax(int a) {
Console.Write("checkMax result of {0}: ", a);
if (a < MAX_VALUE)
Console.WriteLine("max value is valid");
else
Console.WriteLine("max value is not valid");
}


public static void checkMin(int a) {
Console.Write("checkMin result of {0}: ", a);
if (a > MIN_VALUE)
Console.WriteLine("min value is valid");
else
Console.WriteLine("min value is not valid");
Console.WriteLine("");
}
}


public class Driver {
public static void Main(string [] args) {
Test.checkInt(1);
Test.checkMax(1);
Test.checkMin(1);


Test.checkInt(10);
Test.checkMax(10);
Test.checkMin(10);


Test.checkInt(20);
Test.checkMax(20);
Test.checkMin(20);


Test.checkInt(30);
Test.checkMax(30);
Test.checkMin(30);


Test.checkInt(254);
Test.checkMax(254);
Test.checkMin(254);


Test.checkInt(255);
Test.checkMax(255);
Test.checkMin(255);


Test.checkInt(256);
Test.checkMax(256);
Test.checkMin(256);
}
}

Delegates.cs

using System;


public delegate void Valid(int a);


public class Test {
public const int MAX_VALUE = 255;
public const int MIN_VALUE = 10;


public static void checkInt(int a) {
Console.Write("checkInt result of {0}: ", a);
if (a < MAX_VALUE && a > MIN_VALUE)
Console.WriteLine("max and min value is valid");
else
Console.WriteLine("max and min value is not valid");
}


public static void checkMax(int a) {
Console.Write("checkMax result of {0}: ", a);
if (a < MAX_VALUE)
Console.WriteLine("max value is valid");
else
Console.WriteLine("max value is not valid");
}


public static void checkMin(int a) {
Console.Write("checkMin result of {0}: ", a);
if (a > MIN_VALUE)
Console.WriteLine("min value is valid");
else
Console.WriteLine("min value is not valid");
Console.WriteLine("");
}
}


public class Driver {
public static void Main(string [] args) {
Valid v1 = new Valid(Test.checkInt);
v1 += new Valid(Test.checkMax);
v1 += new Valid(Test.checkMin);
v1(1);
v1(10);
v1(20);
v1(30);
v1(254);
v1(255);
v1(256);
}
}

An example might be as seen here. You have a method to process an object that meets certain requirements. However, you want to be able to process the object in multiple ways. Instead of having to create separate methods, you can simply assign a matching method that processes the object to a delegate and pass the delegate to the method that selects the objects. That way, you can assign different methods to the one selector method. I tried to make this easily understandable.