Best practices with events in C#
In C#, you can have a return value in your event function. However, you only receive the return 开发者_如何学Govalue of the last event. Also, there doesn't appear to be a way to get the return value of the previous event.
What are some good practices? Should I always use void
? From my limited experience if I want to chain values must I use ref
?
How might I write an event? I wanted to use Func<ref t, returnT>, but ref
is illegal there, and I imagine action is the same way. (I ended up with the below). Is there a way to make the event one line instead of two when using a ref
?
delegate int FuncType(ref int a);
static event FuncType evt;
static void Main(string[] args)
{
evt += foo;
var aa = 1;
var a = evt(ref aa);
evt += bar;
var bb = 1;
var b = evt(ref bb);
}
static int foo(ref int a)
{
a = a*3;
return a;
}
static int bar(ref int a)
{
a=a +1;
return a;
}
As said you can use GetInvocationList
which will allow you to call each method individually and process returned data.
But before that please consider using EventHandler<T>
with EventArgs
.
You can have "everything" you need to be returned in EventArgs
.
Check this sample code :
public class BalanceChangedEventArgs : EventArgs
{
public readonly double OldBalance;
public readonly double NewBalance;
public BalanceChangedEventArgs(double oldB, double newB)
{
OldBalance = oldB;
NewBalance = newB;
}
}
public class Account
{
private double balance;
public EventHandler<BalanceChangedEventArgs> balanceChanged;
protected void OnBalanceChanged(BalanceChangedEventArgs eArgs)
{
if (balanceChanged != null)
balanceChanged(this, eArgs);
}
public double Balance
{
get { return balance; }
set
{
if (balance == value)
return;
OnBalanceChanged(new BalanceChangedEventArgs(balance, value));
balance = value;
}
}
}
Don't confuse "event" with "callback". If you're wanting to provide a "hook" for customization, then consider one of the following:
- A base class with virtual methods for hooks.
- An interface for a callback object passed in to your constructor or accessed via a property.
- A delegate passed in to your constructor or accessed via a property.
If you've considered the above, and still want to use an event, then you could include the "result" as part of your event argument type, e.g., e.Result
or e.Handled
. You still have the issue of multiple event handlers possibly overwriting each other's values, so you should combine that approach with iterating the invocation list as suggested by other answers. Either collate all the results or have an "early exit" strategy like what is used for e.Handled
.
You may want to look at Microsoft's documentation for Event Design.
If you want to get the return values when an event has multiple subscribers, use Delegate.GetInvocationList
. Then you can say
foreach(FuncType d in evt.GetInvocationList()) {
int value = d(parameter);
// do something with value
}
However, in general, it's best to avoid return values in event handlers.
The 'best practice' is to only use void
eventhandlers. Precisely because of the last-value-only problem.
If you want combined results, define an EventArgs descendant with appropriate properties. Use a list or sum values or something.
精彩评论