是否使用委托来帮助处理某些异步情况?我尝试了以下但我的UI仍然挂起。在地球上你是否使用代表?
Public Class Form1
Private Delegate Sub testDelegate()
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles Button1.Click
Dim d As testDelegate = New testDelegate(AddressOf Add)
d.Invoke()
End Sub
Private Sub Add()
For i As Integer = 0 To 10000
TextBox1.Text = i + 1
Next
End Sub
End Class
答案 0 :(得分:7)
正如Joel所提到的 - BeginInvoke()将异步执行委托 - 如果目标返回数据(使用EndInvoke),您将需要设置Async回调来检索返回值。
以下链接是关于使用Delegates for Async Programming的好文章:http://msdn.microsoft.com/en-us/library/2e08f6yc.aspx
另外(这是在C#中 - 抱歉)你可以使用lambda表达式来处理回调:
Action myAction = () => Console.WriteLine("This is an async call!");
myAction.BeginInvoke(asyncResult => Console.WriteLine("Async Done!"), null);
答案 1 :(得分:5)
这有点讽刺,但每个人最喜欢的答案(使用BeginInvoke)实际上并不正确。委托目标方法将在线程池线程上执行。您不能在创建它们的线程以外的线程上触摸控件,几乎总是程序的主线程。
如果在调试器中使用.NET framework 2.0及更高版本进行尝试,则循环将立即以IllegalOperationException终止。要纠正这个问题,您必须使用Control.BeginInvoke()。与委托的BeginInvoke()方法完全不同的动物btw。
现在有了讽刺意味,你的循环现在会向UI线程发送10,000个委托调用请求。它将花费几秒钟执行它们,而不是开始做任何实际工作。就像调度TextBox的Paint事件一样。或响应任何用户输入。事实上,你比以前更糟糕了。
我怀疑这有助于解释代表们。也许你可以选择一个更好的例子,一些不试图更新控件的东西。
答案 2 :(得分:2)
这是我发给同事的事情:
基本上,委托可用于回调,事件和异步处理。
以下是一些示例代码,将每个示例复制并粘贴到新的命令行项目中。
//Callbacks
//-------------------------------------------------------------------------------------------------------
delegate double DiscountRule(); // Declaration
delegate string MessageFormat(); // Declaration
class Message
{
public string Instance() { return "You save {0:C}"; }
public static string Class() { return "You are buying for {0:C}"; }
public void Out(MessageFormat format, double d)
{
System.Console.WriteLine(format(), d);
}
}
class Discount
{
public static double Apply(DiscountRule rule, double amount)
{
return rule() * amount; // Callback
}
public static double Maximum() { return 0.50; }
public static double Average() { return 0.20; }
public static double Minimum() { return 0.10; }
public static double None() { return 0.00; }
}
class TestDelegate1
{
public static void Main()
{
DiscountRule[] rules = { // Instantiations
Discount.None,
Discount.Minimum,
Discount.Average,
Discount.Maximum,
};
// Instantiation with a static method
MessageFormat format = Message.Class;
double buy = 100.00;
Message msg = new Message();
msg.Out(format, buy); // Invocation
// Instantiation with an instance method
format = msg.Instance;
foreach (DiscountRule r in rules)
{
double saving = Discount.Apply(r, buy); // Invocation
msg.Out(format, saving); // Invocation
}
}
}
//--------------------------------------------------------------------------------------------------------
//Events:
//the delegate creates a new type
delegate void UpdateEventHandler();
class Subject
{
private int data;
public int GetData() { return data; }
public void SetData(int value) { data = value; Changed(); }
public event UpdateEventHandler Changed;
}
class Observer
{
public Observer(Subject s) { subject = s; }
public Subject GetSubject() { return subject; }
private Subject subject;
}
class HexObserver : Observer
{
public HexObserver(Subject s)
: base(s)
{
s.Changed += Update;
}
public void Update()
{
System.Console.Write("0x{0:X} ", GetSubject().GetData());
}
}
class DecObserver : Observer
{
public DecObserver(Subject s)
: base(s)
{
s.Changed += Update;
}
public void Update()
{
System.Console.Write("{0} ", GetSubject().GetData());
}
}
class TestEvent
{
public static void Main()
{
Subject s = new Subject();
//assigns a Hex observer to object s (the constructor does the += assignment)
HexObserver ho = new HexObserver(s);
//assigns a Decimal observer to object s
DecObserver co = new DecObserver(s);
for (int c; ; )
{
System.Console.Write("\nEnter a character" +
"(followed by a return, ctrl-C to exit): ");
c = System.Console.Read();
s.SetData(c);
System.Console.Read(); // Two reads to get rid of the \r\n on PC.
System.Console.Read();
}
}
}
//--------------------------------------------------------------------------------------------------------
//Asynchronous processing (from http://msdn.microsoft.com/en-us/library/h80ttd5f(VS.71).aspx)
using System;
using System.Runtime.Remoting.Messaging;
// Create an asynchronous delegate.
public delegate bool FactorizingAsyncDelegate (
int factorizableNum,
ref int primefactor1,
ref int primefactor2);
// Create a class that factorizers the number.
public class PrimeFactorizer
{
public bool Factorize(
int factorizableNum,
ref int primefactor1,
ref int primefactor2)
{
primefactor1 = 1;
primefactor2 = factorizableNum;
// Factorize using a low-tech approach.
for (int i=2;i<factorizableNum;i++)
{
if (0 == (factorizableNum % i))
{
primefactor1 = i;
primefactor2 = factorizableNum / i;
break;
}
}
if (1 == primefactor1 )
return false;
else
return true ;
}
}
// Class that receives a callback when the results are available.
public class ProcessFactorizedNumber
{
private int _ulNumber;
public ProcessFactorizedNumber(int number)
{
_ulNumber = number;
}
// Note that the qualifier is one-way.
[OneWayAttribute()]
public void FactorizedResults(IAsyncResult ar)
{
int factor1=0, factor2=0;
// Extract the delegate from the AsyncResult.
FactorizingAsyncDelegate fd = (FactorizingAsyncDelegate)((AsyncResult)ar).AsyncDelegate;
// Obtain the result.
fd.EndInvoke(ref factor1, ref factor2, ar);
// Output the results.
Console.WriteLine("On CallBack: Factors of {0} : {1} {2}",
_ulNumber, factor1, factor2);
}
}
// Class that shows variations of using Asynchronous
public class Simple
{
// The following demonstrates the Asynchronous Pattern using a callback.
public void FactorizeNumber1()
{
// The following is the client code.
PrimeFactorizer pf = new PrimeFactorizer();
FactorizingAsyncDelegate fd = new FactorizingAsyncDelegate (pf.Factorize);
int factorizableNum = 1000589023, temp=0;
// Create an instance of the class that is going
// to be called when the call completes.
ProcessFactorizedNumber fc = new ProcessFactorizedNumber(factorizableNum);
// Define the AsyncCallback delegate.
AsyncCallback cb = new AsyncCallback(fc.FactorizedResults);
// You can use any object as the state object.
Object state = new Object();
// Asynchronously invoke the Factorize method on pf.
IAsyncResult ar = fd.BeginInvoke(
factorizableNum,
ref temp,
ref temp,
cb,
state);
//
// Do some other useful work.
//. . .
}
// The following demonstrates the Asynchronous Pattern using a BeginInvoke, followed by waiting with a time-out.
public void FactorizeNumber2()
{
// The following is the client code.
PrimeFactorizer pf = new PrimeFactorizer();
FactorizingAsyncDelegate fd = new FactorizingAsyncDelegate (pf.Factorize);
int factorizableNum = 1000589023, temp=0;
// Create an instance of the class that is going
// to be called when the call completes.
ProcessFactorizedNumber fc = new ProcessFactorizedNumber(factorizableNum);
// Define the AsyncCallback delegate.
AsyncCallback cb =
new AsyncCallback(fc.FactorizedResults);
// You can use any object as the state object.
Object state = new Object();
// Asynchronously invoke the Factorize method on pf.
IAsyncResult ar = fd.BeginInvoke(
factorizableNum,
ref temp,
ref temp,
null,
null);
ar.AsyncWaitHandle.WaitOne(10000, false);
if (ar.IsCompleted)
{
int factor1=0, factor2=0;
// Obtain the result.
fd.EndInvoke(ref factor1, ref factor2, ar);
// Output the results.
Console.WriteLine("Sequential : Factors of {0} : {1} {2}",
factorizableNum, factor1, factor2);
}
}
public static void Main(String[] args)
{
Simple simple = new Simple();
simple.FactorizeNumber1();
simple.FactorizeNumber2();
}
答案 3 :(得分:2)
致电.BeginInvoke()
而不是.Invoke()
。
答案 4 :(得分:2)
我发现解释代表的最佳比喻是遗嘱或遗嘱。
这是一套指示,当然你在死之前就写了,然后把它放在一个安全的地方。在你去世后,你的律师将执行那些指示......
代理主要用于想要执行操作的代码不知道该操作应该是什么的足够详细信息,您可以将其视为在适当的时间执行的一系列操作。
答案 5 :(得分:1)
在这种情况下,我可能不会使用委托,因为您需要重新委托回UI线程来更新按钮。您可以在循环中使用Application.DoEvents,以便在更新时响应UI。
答案 6 :(得分:1)
正如Joel和Matthew所说,BeginInvoke将异步执行委托。正如马修所说,this MSDN article很好地涵盖了这一点。
但是,如果您尝试在WinForms应用程序中执行某些后台工作,我建议您查看background worker control。对于习惯于WinForms事件驱动模型的开发人员来说,它会更加熟悉。