Friday, April 4, 2008

Unneccessay EventArgs

Disclaimer: I am not actually doing this now and it's just something to think about and is probably just an obsessive detail. While many say unnecessary or premature optimization is the root of all evil, still, as responsible programmers, we need to keep performance in mind whenever we do things. And while this example may be frivolous, it shows a pattern you can use in other, more important scenarios.

I'm sure you all would agree that the unnecessary creation of objects is not good. Wasting is something we should try to avoid. For example, wouldn't it strike you as wrong to create EventArgs objects for events that are never raised?

Take this code, which is a basic event to alert the application code when a user signs in. The event args gives the developer some more info about the user who logged in:


internal class SigninManager

{

public event EventHandler<SignedInEventArgs> SignedIn;

protected virtual void OnSignedIn(SignedInEventArgs e)

{

if (SignedIn != null)

SignedIn(this, e);

}

private void PerformSignIn()

{

//work work work

string userName = "blah";

//work work work

SignedInEventArgs args = new SignedInEventArgs() { UserName = userName };

OnSignedIn(args);

}

}

public class SignedInEventArgs: EventArgs

{

public string UserName { get; internal set; }

}



Note I'm using some C# 3 features, but this could be done with a prior version with not much work.

In OnSignedIn, you'll notice the event will never get invoked if Fooing is null. The event is only for informational purposes to outside code. If I wanted some feedback, such as a Handled property, things might be different. But I just want to tell application code that "hey, this person logged in."

If no outside code ever handles SignedIn, I am creating the SignedInEventArgs without ever using it. What I would prefer is that the event args get created only if they are needed. But I want to keep my code pretty much the same. I want to keep my OnSignedIn there so I can override it in a sub class. And I don't want to fool with event logic in PerformSignIn. What I need is "lazy instantiation", where the event args only get created if needed.

While there are several approaches, I have a pretty elegant one: use delegates. I will use lamda expressions in my code, although anonymous delegates would work fine. Check this out:

public delegate TEventArgs EventHandlerCreator<TEventArgs>();

internal class SigninManager

{

public event EventHandler<SignedInEventArgs> SignedIn;

protected virtual void OnSignedIn(EventHandlerCreator<SignedInEventArgs> argsDelegate)

{

if (SignedIn != null)

{

SignedIn(this, argsDelegate());

}

}

private void PerformSignIn()

{

//work work work

string userName = "blah";

//work work work

OnSignedIn( () => new SignedInEventArgs() { UserName = userName } );

}

}

public class SignedInEventArgs: EventArgs

{

public string UserName { get; internal set; }

}

Now, instead of passing the event args object I created, I pass a delegate that's able to create the args when invoked. So the OnSigningIn takes that delegate and only creates the args if the event is handled. If it is invoked a million times, we've saved a million objects :).

I think the code is pretty straightforward. Lamda expressions and generics make "lazy instantiation" something that's practical to acheive and maintain. Again, this could be applied to different, more meaningful situations. Regardless, it's pretty nice.

1 comment:

Francisco Padron said...

Depending on the size of your EventArgs, creating a delegate may be equally (or more?) costly.

If the EventArgs is big (a lot of fields) then the creating the delegate will probably be better but if the EventArgs is small is probably not.

This is an issue that has anoyed me for a while too.

Frank
http://frankpadron.blogspot.com/