【发布时间】:2008-10-10 13:02:06
【问题描述】:
您在 C# 中如何使用委托?
【问题讨论】:
-
您的意思是 .NET 类型系统中的委托还是 C# 委托语法?你的意思是“你什么时候使用委托语法而不是 lambda 表达式语法”还是你的意思是“你什么时候使用委托而不是类/接口/虚拟方法/等”?
您在 C# 中如何使用委托?
【问题讨论】:
委托通常可以用一种方法代替接口,一个常见的例子就是观察者模式。在其他语言中,如果您想收到发生某事的通知,您可以定义如下内容:
class IObserver{ void Notify(...); }
在 C# 中,这更常使用事件来表达,其中处理程序是委托,例如:
myObject.SomeEvent += delegate{ Console.WriteLine("..."); };
另一个使用委托的好地方,如果您必须将谓词传递给函数,例如从列表中选择一组项目时:
myList.Where(i => i > 10);
上面是一个 lambda 语法的例子,也可以写成如下:
myList.Where(delegate(int i){ return i > 10; });
另一个可以使用委托的地方是注册工厂函数,例如:
myFactory.RegisterFactory(Widgets.Foo, () => new FooWidget());
var widget = myFactory.BuildWidget(Widgets.Foo);
我希望这会有所帮助!
【讨论】:
【讨论】:
委托对许多用途都非常有用。
其中一个目的是使用它们来过滤数据序列。在这种情况下,您将使用一个谓词委托,它接受一个参数并根据委托本身的实现返回 true 或 false。
这是一个愚蠢的例子——我相信你可以从中推断出更有用的东西:
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");
}
}
【讨论】:
static Boolean inMyFamily(String name) 方法是委托。 Where 将委托作为参数。因为当您将方法名称传递给成为委托的.Where(delegate) 时,委托只是函数指针。由于 inMyFamily 返回一个布尔类型,它实际上被认为是一个谓词。谓词只是返回布尔值的委托。
现在我们在 C# 中有 lambda 表达式和匿名方法,我更多地使用委托。在 C# 1 中,您总是必须有一个单独的方法来实现逻辑,使用委托通常没有意义。这些天,我使用委托来:
【讨论】:
您可以使用委托来声明函数类型的变量和参数。
示例
考虑“资源借用”模式。您希望控制资源的创建和清理,同时允许客户端代码在两者之间“借用”资源。
这声明了一个委托类型。
public delegate void DataReaderUser( System.Data.IDataReader dataReader );
任何匹配此签名的方法都可用于实例化此类型的委托。在 C# 2.0 中,这可以隐式完成,只需使用方法的名称,也可以使用匿名方法。
此方法使用类型作为参数。注意委托的调用。
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;
}
}
}
可以使用匿名方法调用该函数,如下所示。请注意,匿名方法可以使用在其自身外部声明的变量。这非常方便(虽然这个例子有点做作)。
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] );
} );
【讨论】:
我来晚了,但我今天无法弄清楚代表的目的,并编写了两个简单的程序,它们给出的输出相同,我认为很好地解释了它们的目的。
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);
}
}
【讨论】:
稍微不同的用途是加速反射;即不是每次都使用反射,您可以使用Delegate.CreateDelegate 为方法(MethodInfo)创建一个(类型化的)委托,然后调用该委托。这样每次调用会快得多,因为检查已经完成。
使用Expression,您还可以执行相同的操作来动态创建代码 - 例如,您可以轻松创建一个Expression,它代表在运行时选择的类型的 + 运算符(为泛型提供运算符支持,该语言不提供);并且您可以将Expression 编译为类型化委托 - 工作完成。
【讨论】:
在您使用事件的任何时候都会使用委托 - 这就是它们的工作机制。
此外,委托对于诸如使用 LINQ 查询之类的事情非常有用。例如,许多 LINQ 查询采用可用于过滤的委托(通常为 Func<T,TResult>)。
【讨论】:
为事件订阅事件处理程序
【讨论】:
例如here。您有一种方法来处理满足某些要求的对象。但是,您希望能够以多种方式处理对象。不必创建单独的方法,您可以简单地将处理对象的匹配方法分配给委托,并将委托传递给选择对象的方法。这样,您可以将不同的方法分配给一个选择器方法。我试图让这很容易理解。
【讨论】:
我使用委托与线程通信。
例如,我可能有一个下载文件的 win forms 应用程序。该应用程序启动一个工作线程来进行下载(这可以防止 GUI 锁定)。工作线程使用委托将状态消息(例如下载进度)发送回主程序,以便 GUI 可以更新状态栏。
【讨论】:
用于事件处理程序
在方法参数中传递方法
【讨论】:
第一行的用法是替换Observer/Observable(事件)模式。第二个,一个漂亮优雅的策略模式版本。可以收集各种其他用法,但比我认为的前两个更深奥。
【讨论】:
事件,其他anynch操作
【讨论】:
任何时候你想封装行为,但以统一的方式调用它。事件处理程序、回调函数等。您可以使用接口和强制转换来完成类似的事情,但有时,行为不一定与 type 或 object 相关联。有时你只是需要封装一些行为。
【讨论】:
延迟参数初始化!除了所有先前的答案(策略模式、观察者模式等)之外,委托还允许您处理参数的延迟初始化。例如,假设您有一个函数 Download(),它需要相当多的时间并返回某个 DownloadedObject。此对象由存储根据特定条件消耗。通常,您会:
storage.Store(conditions, Download(item))
但是,对于委托(更准确地说,lambdas),您可以通过更改 store 的签名来执行以下操作,以便它接收 Condition 和 Func
storage.Store(conditions, (item) => Download(item))
因此,存储只会在必要时评估委托,根据条件执行下载。
【讨论】:
委托的使用
【讨论】:
In Array.Sort(T[] array,Comparison comparison)、List.Sort(Comparison comparison)等中的比较参数
【讨论】:
据我所知,委托可以转换为函数指针。这使得与采用函数指针的本机代码进行互操作时变得更加轻松,因为它们可以有效地面向对象,即使最初的程序员没有为此做任何准备。
【讨论】:
委托用于通过引用来调用方法。 例如:
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();
}
}
【讨论】: