Archive for C#

Spearmen, Javelin Throwers, and the State Pattern. Oh My!

// August 15th, 2009 // 6 Comments » // C#

Last week one of Ayende/Oren’s posts caught my eye: Let the fighting commence! In it he discussed a blog post seen on Jamie Farser’s blog, where Jamie discusses some design difficulties he has run into while designing a turn-based game similar to Axis & Allies named Everland. Since then I’ve been following Jamie’s progress (first try, further exploration, second try, third try, even further exploration) as he tries to take some of Ayende’s advice and implement the state pattern. Like Jamie, I am fairly new to the state pattern – sure I’ve read about it plenty (starting in Head First Design Patterns – a must have!), but I honestly have just never implement it “for reals.” In my back & forth with Jamie I realized that it was time to put up or shut up – if I’m going to offer criticism I should also offer up an alternative solution.

The Problem

Let’s say we have a game where the board is made up of six-side polygons (“hexes”, if you will) and each hex can contain a unit like a spearman or a javelin thrower. Each unit on the board belongs to a player who can take turns moving his units, telling his units to attack other player’s units, etc. Some units have different capabilities than other units – a spearman must be within 1 hex of another unit to attack that unit, whereas a javelin thrower can be 2 units away. How do we design the application in such a way that:

  • The game engine doesn’t need to know specifics about each unit (such as range of attack).
  • A unit’s capabilities may change during the course of gameplay (such as a spearman being upgraded to a javelin thrower).

My Solution

Before we look at the code, lets run through what is going to happen in-game.

  1. The user selects one of their units, then selects an opposing unit somewhere else on the gameboard, then clicks an “Attack” button.
  2. The game engine creates a context (deriving from ITurnContext) that describes what the user has asked to do. In our case, an AttackContext is created containing references to the attacking unit and the defending unit.
  3. That context is then passed into the ExecuteTurn method of the attacking Unit, which then passes it along into the Handle method of its CurrentState property (which is an object deriving from UnitState). Our attacker’s CurrentState property is currently Spearman, but could just as easily be JavelinThrower. Changing a unit from melee to ranged is as simple as changing the state of that unit.
  4. Our Spearman state derives from UnitState, and the UnitState base class’ Handle method uses double dispatch to forward the Handle request on to the appropriate method of our subclass. In our case that would be the Handle(AttackContext) method of the Spearman class.
  5. Our Spearman class’ Handle method then checks to see if the unit is within range to attack the defending unit – if it is, an AttackCommand is returned denoting who the attacker and the defender are; if it isn’t, a MovementCommand is returned denoting which hex to move to.
  6. The Unit class then calls Execute on the returned command, causing the movement or attack operation to be, well, executed.

Let’s go through each of the above steps and take a look at the code involved one by one. Skipping the first step (the UI), we’ll move on to where the game engine is creating an AttackContext and passing it into the attacking Unit:

Attacker.ExecuteTurn(new AttackContext {Attacker = Attacker, Defender = Defender});

Okay, easy enough. Moving on we can see the Unit class’ ExecuteTurn method pass the context to its CurrentState property’s Handle method:

public class Unit
{
	public Hex Location { get; set; }
	public int Health { get; set; }
	public UnitState CurrentState { get; set; }

	public void ExecuteTurn(ITurnContext context)
	{
		CurrentState.Handle(context).Execute();
	}
}

Our attacking unit’s CurrentState property is currently set to an instance of Spearman, but before we take a look at that class we’ll take a look at its base class (UnitState - which is doing double dispatch to the Spearman‘s Handle method):

public abstract class UnitState
{
	private static readonly Dictionary Dispatch = new Dictionary();

	static UnitState()
	{
		foreach (var t in Assembly.GetCallingAssembly().GetTypes().Where(t => t.IsSubclassOf(typeof (UnitState))))
			foreach (var mi in t.GetMethods().Where(mi => mi.Name == "Handle" && mi.GetParameters().Length > 0))
				Dispatch.Add(((Int64) t.GetHashCode() << 32) + mi.GetParameters()[0].ParameterType.GetHashCode(), mi);
	}

	public IUnitCommand Handle(ITurnContext context)
	{
		var hash = ((Int64) GetType().GetHashCode() << 32) + context.GetType().GetHashCode();
		return Dispatch.ContainsKey(hash)
		       	? Dispatch[hash].Invoke(this, new[] {context}) as IUnitCommand
		       	: new NullCommand();
	}
}

The Spearman class’ Handle method is then invoked:

public class Spearman : UnitState
{
	public IUnitCommand Handle(AttackContext context)
	{
		if (context.Attacker.Location.DistanceTo(context.Defender.Location) == 1)
			return new AttackCommand(context);
		return new MovementCommand(new MovementContext
		                           	{
		                           		UnitToMove = context.Attacker,
		                           		HexToMoveTo =
		                           			context.Attacker.Location .FindClosestSurroundingHexTo( context.Defender.Location)
		                           	});
	}
}

Notice that the only concern Spearman currently has when handling an attack context is ensuring that it gets within range and then attacks the target. We’ll see an example of a JavelinThrower later. Finally we’ll peek at what the MovementCommand and AttackCommand actually do:

public class MovementCommand : IUnitCommand
{
	private readonly MovementContext _movementContext;

	public MovementCommand(MovementContext movementContext)
	{
		_movementContext = movementContext;
	}

	#region IUnitCommand Members

	public void Execute()
	{
		_movementContext.UnitToMove.Location = _movementContext.HexToMoveTo;
	}

	#endregion
}

public class AttackCommand : IUnitCommand
{
	private readonly AttackContext _attackContext;

	public AttackCommand(AttackContext attackContext)
	{
		_attackContext = attackContext;
	}

	#region IUnitCommand Members

	public void Execute()
	{
		_attackContext.Defender.Health -= 10;
	}

	#endregion
}

Before we move on, here is the promised JavelinThrower class:

public IUnitCommand Handle(AttackContext context)
{
	if (context.Attacker.Location.DistanceTo(context.Defender.Location) <= 2)
		return new AttackCommand(context);
	return new MovementCommand(new MovementContext
	                           	{
	                           		UnitToMove = context.Attacker,
	                           		HexToMoveTo =
	                           			context.Attacker.Location .FindClosestSurroundingHexTo( context.Defender.Location)
	                           	});
}

Obviously JavelinThrower & Spearman currently only differ in the range at which they can attack (<=2 and <= 1, respectively), so the code in their Handle methods could be abstracted out using the Strategy Pattern.

Prove It!

So now that we’ve had a chance to see the code involved the question comes down to: does it actually work? Here are our unit tests:

[Fact]
public void JavelinThrower_moves_to_two_hexes_away_and_attacks()
{
	//starting point
	Assert.Equal(4, Attacker.Location.DistanceTo(Defender.Location));
	Assert.Equal(100, Defender.Health);

	//turn one - moves closer
	Attacker.ExecuteTurn(new AttackContext { Attacker = Attacker, Defender = Defender });
	Assert.Equal(3, Attacker.Location.DistanceTo(Defender.Location));
	Assert.Equal(100, Defender.Health);

	//turn two - moves closer
	Attacker.ExecuteTurn(new AttackContext { Attacker = Attacker, Defender = Defender });
	Assert.Equal(2, Attacker.Location.DistanceTo(Defender.Location));
	Assert.Equal(100, Defender.Health);

	//turn three - attacks
	Attacker.ExecuteTurn(new AttackContext { Attacker = Attacker, Defender = Defender });
	Assert.Equal(2, Attacker.Location.DistanceTo(Defender.Location));
	Assert.Equal(90, Defender.Health);
}

[Fact]
public void Spearman_moves_to_one_hex_away_and_attacks()
{
	//starting point
	Assert.Equal(4, Attacker.Location.DistanceTo(Defender.Location));
	Assert.Equal(100, Defender.Health);

	//turn one - moves closer
	Attacker.ExecuteTurn(new AttackContext {Attacker = Attacker, Defender = Defender});
	Assert.Equal(3, Attacker.Location.DistanceTo(Defender.Location));
	Assert.Equal(100, Defender.Health);

	//turn two - moves closer
	Attacker.ExecuteTurn(new AttackContext {Attacker = Attacker, Defender = Defender});
	Assert.Equal(2, Attacker.Location.DistanceTo(Defender.Location));
	Assert.Equal(100, Defender.Health);

	//turn three - moves closer
	Attacker.ExecuteTurn(new AttackContext {Attacker = Attacker, Defender = Defender});
	Assert.Equal(1, Attacker.Location.DistanceTo(Defender.Location));
	Assert.Equal(100, Defender.Health);

	//turn four - attacks
	Attacker.ExecuteTurn(new AttackContext {Attacker = Attacker, Defender = Defender});
	Assert.Equal(1, Attacker.Location.DistanceTo(Defender.Location));
	Assert.Equal(90, Defender.Health);
}

And the results…

Spearman & Javelin Thrower Tests Passed!

Yay!

So that is the road I went down, but what I really want to know is how would you solve this problem? If you’d like to take a deeper look at my code, download it and give it a try yourself:

EverlandStatePattern.zip

Kick It on DotNetKicks.comShout It on DotNetShoutOuts.com

Return of the PagedList

// June 15th, 2009 // 12 Comments » // C#, LINQ, MVC

It has been nearly a year since I posted an updated version of the PagedList<T> functionality originally created by Scott Guthrie and posted by Rob Conery. Since then I have used the class in a number of projects and find it indispensable.

A few days ago, Craig Stuntz reported an interesting observation: when the first page is returned, the class performs a Skip(0). Suprisingly, this is not free. With that in mind, I set out to correct that issue as well as incorporate a few changes I’ve made over the past year. The result is nearly identical to the last posted version, just a bit more readable. Additionally…
(more…)

Kick It on DotNetKicks.comShout It on DotNetShoutOuts.com

PagedList Strikes Back

// July 8th, 2008 // 9 Comments » // C#

There is an updated version of the PagedList<T> code available here.

A few months ago I posted about my changes to Rob Conery’s PagedList class. Since writing that article many comments have been left about how to further improve the design, which I have since incorporated into a new, further improved PagedList class. For those who aren’t familiar, the PagedList class allows scenarios such as the following:

   1: public void ListProducts( int pageIndex )
   2: {
   3:     int pageSize = 10;
   4:     var products = productRepository.GetAllProducts()
   5:         .ToPagedList( pageIndex, pageSize );
   6:     return View( products );
   7: }

(more…)

Kick It on DotNetKicks.comShout It on DotNetShoutOuts.com

OpenID Check_Authentication In C#

// April 11th, 2008 // 12 Comments » // C#

Earlier this year Mads Krisensen (of BlogEngine.net fame) posted a lightweight implementation of OpenID using C#. In the comments on Mads’ post, Andrew Arnott (a developer of the DotNetOpenId library) mentioned that the example Mads had posted could "be hacked with a single change of a word in the URL." This is what is technically referred to as a Very Bad Thing. Andrew and another poster named "neil" went on to elaborate that implementing OpenID’s "check_authentication" algorithm would close this security hole. Unfortunately as of the writing of this article neither Mads nor any of the commentors have provided an implementation of check_authentication that works with the class Mads posted (bear in mind that Andrew only brought up this issue less than a week ago, so Mads may very well be working on it).

Fast forward to yesterday when I was researching my options for implementing OpenID for the next release of the ASP.Net MVC Membership Starter Kit. I liked Mads solution more than the other OpenID libraries that are current available because of its brevity and how easy it is to include it in a project without introducing an extra assembly dependency, so I decided to go ahead and add the check_authentication functionality. A quick read of that portion of the OpenID spec and a couple hours of coding/testing and I think I’m about finished.

Here is the method you need to add to Mads’ class:

   1: private static bool CheckAuthentication( NameValueCollection query )
   2: {
   3:  
   4:     //### get data required for check_authentication
   5:     string mode = "check_authentication";
   6:     string handle = query["openid.assoc_handle"];
   7:     string signature = query["openid.sig"];
   8:     string signed = query["openid.signed"];
   9:     string extra = string.Empty;
  10:  
  11:     //### loop through fields required by "openid.signed" and retrieve that data
  12:     if( !string.IsNullOrEmpty(signed) )
  13:     {
  14:         string[] exemptions = { "mode", "assoc_handle", "sig", "signed" };
  15:         string[] fields = signed.Split(',');
  16:         foreach( string field in fields )
  17:         {
  18:             if( exemptions.Contains(field) ) continue;
  19:             extra += string.Format( "openid.{0}={1}&", field, HttpUtility.UrlEncode( query[ "openid." + field ] ) );
  20:         }
  21:         extra = "&" + extra.Substring( 0, extra.Length - 1 );
  22:     }
  23:  
  24:     //### combine all the data together to form the request
  25:     string post = string.Format( "openid.mode={0}&openid.assoc_handle={1}&openid.sig={2}&openid.signed={3}{4}",
  26:         mode,
  27:         HttpUtility.UrlEncode( handle ),
  28:         HttpUtility.UrlEncode( signature ),
  29:         HttpUtility.UrlEncode( signed ),
  30:         extra
  31:     );
  32:  
  33:     //### begin sending request
  34:     HttpWebRequest request = (HttpWebRequest)WebRequest.Create( query["openid.op_endpoint"] );
  35:     request.Method = "POST";
  36:     request.ContentType = "application/x-www-form-urlencoded";
  37:     request.ContentLength = post.Length;
  38:  
  39:     //### transmit POST data
  40:     using( StreamWriter sw = new StreamWriter(request.GetRequestStream()) )
  41:         sw.Write(post);
  42:  
  43:     //### get response
  44:     string html = "";
  45:     using( HttpWebResponse response = (HttpWebResponse)request.GetResponse() )
  46:         using( StreamReader sr = new StreamReader( response.GetResponseStream() ) )
  47:             html = sr.ReadToEnd();
  48:  
  49:     //### determine if check_authentication passed or not
  50:     if( string.IsNullOrEmpty(html) || !html.StartsWith( "is_valid:" ) || html.StartsWith( "is_valid:false" ) )
  51:         return false;
  52:     else if( html.StartsWith( "is_valid:true" ) )
  53:         return true;
  54:     else
  55:         throw new InvalidOperationException( "Unexpected return from OpenID check_authentication." );
  56:  
  57: }

Now you need to make sure that method is called from somewhere. I chose to make the method private and just call it from inside the Authenticate method. Within the Authenticate method replace:

   1: // Make sure the incoming request's identity matches the one stored in session
   2: if( query["openid.claimed_id"] != data.Identity )
   3:     return data;

… with…

   1: // Make sure the incoming request's identity matches the one stored in session
   2: if( query["openid.claimed_id"] != data.Identity )
   3:     return data;
   4: else if( !CheckAuthentication( query ) )
   5:     throw new UnauthorizedAccessException( "OpenID False Verification Detected" );

And that’s it!

If anyone with more experience than I in OpenID waters sees a problem with my implementation, let me know and I will try to get it fixed quickly.

NOTE: I am aware that Mads’ implementation and the use of ‘check_authentication’ is considered an overly chatty use of OpenID. It seems to me that the extra complexity required to implement OpenID 2.0 protocol is just not worthwhile for most OpenID consumers. Feel free to let me know why this is a stupid position to take.

Kick It on DotNetKicks.comShout It on DotNetShoutOuts.com

Rob Conery’s PagedList Class (Updated)

// April 8th, 2008 // 13 Comments » // C#

NOTE:
A new, improved version of this class is now available at:

http://www.squaredroot.com/post/2008/07/08/PagedList-Strikes-Back.aspx

Robert Muehsig has posted a great user control for the MVC framework that adds pagination links to the bottom of a paged list. In it he used a slightly customized version of Rob Conery’s PagedList class that Rob was kind enough to post way back when the first CTP was released. This reminded me that I should probably post the version I have customized, as I think it makes it a bit easier to use and maintain. I’ve included the code below.

   1: using System;
   2: using System.Linq;
   3:  
   4: namespace System.Collections.Generic
   5: {
   6:  
   7:     public interface IPagedList
   8:     {
   9:         int TotalPages { get; }
  10:         int TotalCount { get; }
  11:         int PageIndex { get; }
  12:         int PageSize { get; }
  13:         bool HasPreviousPage { get; }
  14:         bool HasNextPage { get; }
  15:         bool IsFirstPage { get; }
  16:         bool IsLastPage { get; }
  17:     }
  18:  
  19:     public class PagedList<T> : List<T>, IPagedList
  20:     {
  21:  
  22:         public PagedList( IEnumerable<T> source, int index, int pageSize )
  23:         {
  24:  
  25:             //### set source to blank list if source is null to prevent exceptions
  26:             if( source == null )
  27:                 source = new List<T>();
  28:  
  29:             //### set properties
  30:             this.TotalCount = source.Count();
  31:             this.PageSize = pageSize;
  32:             this.PageIndex = index;
  33:             if( this.TotalCount > 0 )
  34:                 this.TotalPages = (int)Math.Ceiling( (double)this.TotalCount / (double)this.PageSize );
  35:             else
  36:                 this.TotalPages = 0;
  37:             this.HasPreviousPage = ( this.PageIndex > 1 );
  38:             this.HasNextPage = ( this.PageIndex < this.TotalPages );
  39:             this.IsFirstPage = ( this.PageIndex == 1 );
  40:             this.IsLastPage = ( this.PageIndex == this.TotalPages );
  41:  
  42:             //### argument checking
  43:             if( index < 1 || index > this.TotalPages )
  44:                 throw new ArgumentOutOfRangeException( "PageIndex out of range." );
  45:             if( pageSize < 1 )
  46:                 throw new ArgumentOutOfRangeException( "PageSize cannot be less than 1." );
  47:  
  48:             //### add items to internal list
  49:             if( this.TotalCount > 0 )
  50:                 this.AddRange( source.Skip( ( index - 1 ) * pageSize ).Take( pageSize ).ToList() );
  51:  
  52:         }
  53:  
  54:         public int TotalPages { get; private set; }
  55:         public int TotalCount { get; private set; }
  56:         public int PageIndex { get; private set; }
  57:         public int PageSize { get; private set; }
  58:         public bool HasPreviousPage { get; private set; }
  59:         public bool HasNextPage { get; private set; }
  60:         public bool IsFirstPage { get; private set; }
  61:         public bool IsLastPage { get; private set; }
  62:  
  63:     }
  64:  
  65:     public static class Pagination
  66:     {
  67:         public static PagedList<T> ToPagedList<T>( this IEnumerable<T> source, int index, int pageSize )
  68:         {
  69:             return new PagedList<T>( source, index, pageSize );
  70:         }
  71:     }
  72:  
  73: }

Changes from Rob’s version:

  • Added a "TotalPages" property.

    If you’re going to loop through each of the pages to display page navigation, you’ll obviously need this.
  • Changed "IsPreviousPage" to "HasPreviousPage".

    It just sounds better.
  • Changed "IsNextPage" to "HasNextPage".

    See above.
  • Added a "IsFirstPage" property.

    The opposite way of using the above two properties. I prefer this way, but kept the original way for backwards compatibility (except the naming).
  • Added a "IsLastPage" property.

    See above.
  • Changed the first constructor to accept IEnumerable<T> rather than IQueryable<T>.

    I’m not exactly sure why Rob originally made it IQueryable. I’m aware that by passing an IQueryable (LINQ) object to this constructor you’ll avoid retrieving the entire set (only taking the results needed), but since IQueryable inherits from IEnumerable everything should be hunky-dory. He probably had a reason and I’m going to wind up breaking all of my stuff, but IEnumerable is just so much handier. =)
  • Removed the second constructor.

    The second constructor took List<T>, which is unnecessary after changing the first constructor to accept IEnumerable.
  • Cleaned up property declarations a bit.

    Mainly to make the page a bit shorter, but also to prevent the multiple calculations that could happen in the original. Also the original allowed the changing of certain properties after an instance was created, which would put the instance into an inconsistent state.
  • Added argument checking and handled a few exception scenarios more gracefully.

    Trying to make debugging a bit friendlier.
  • Removed the second extension method that didn’t specify a pageSize.

    I don’t really think that baking in an extension method that sets pageSize to 10 is a good idea, I’d prefer pageSize to be explicitly set elsewhere by the calling code.
  • Moved the code to the "System.Collections.Generic" namespace.

    I’m sure a lot of you are breaking out in a cold sweat to see me putting something into a System.* namespace, but I kind of feel like this is something that the .Net team just "forgot". =) Move it wherever makes you comfortable.

Please note that I took many of these ideas from the commentary below Rob’s original post. I’m sure many of you are using something similar, but I thought it would be useful to get something posted online that is a bit more fleshed out than the original example.

Thanks for the great work Rob & Robert!

Kick It on DotNetKicks.comShout It on DotNetShoutOuts.com

Lambdas Using Funcs and Actions

// February 12th, 2008 // 10 Comments » // C#

Many of you are probably familiar with lambda expressions by now, as they have been extensively covered since the release of .Net 3.5. Most articles tend to focus on how to pass a lambda expression into a method, but how do you create a method that accepts a lambda expression? Enter the Func and Action types…

Funcs vs. Actions

Funcs and Actions are extremely similar; both give you first class support for passing an anonymous method into another method. The difference is in the return value: Funcs have one, Actions do not.

A Simple Action Consumer

There are several variants of the Action type, each taking a varying number of generic parameters. The simplest generic Action takes one generic parameters: the type of the object to be passed into the Action.

Suppose you had a list of articles and wanted to iterate through them and modify each of them in some way, but only the ones that were created before 2008. In this case you should probably use a foreach loop, but instead I’ll show you how to do it by encapsulating that foreach loop in another method that then calls your lambda:

   1: public void IterateArticles( Action<Article> process, List<Article> articles )
   2: {
   3:     foreach( Article article in articles )
   4:         if( article.Created.Year < 2008 )
   5:             process(article);
   6: }
   7:
   8: public void ProcessArticles()
   9: {
  10:     List<Article> articles = GetArticles();
  11:     IterateArticles( x=>{
  12:
  13:         x.Title = x.Title.Trim();
  14:         x.Category = x.Category.Trim();
  15:         x.LastModified = DateTime.Now;
  16:
  17:     }, articles );
  18: }

I’ll be the first to admit it isn’t the finest example of coding practices, but I believe it illustrates how you can use an Action to pass processing in as a parameter.

A Simple Func Consumer

What if you want to pass in your processing and retrieve a return value? Use the Func type. Let’s keep the same example as above, but this time we’ll place variable processing in the lambda to manipulate the article’s title differently based upon the year the article was published:

   1: public void IterateArticles( Func<Article,string> process, List<Article> articles )
   2: {
   3:     foreach( Article article in articles )
   4:         article.Title = process(article);
   5: }
   6:
   7: public void ProcessArticles()
   8: {
   9:     List<Article> articles = GetArticles();
  10:     IterateArticles( x=>{
  11:
  12:         if( x.Created.Year < 2008 )
  13:             return x.Title.ToLower();
  14:         else
  15:             return x.Title.ToUpper();
  16:
  17:     }, articles );
  18: }

In the above example you’ll see that we have changed Action to Func in the IterateArticles method signature and that we have added a string to the list of types in the Func definition. When using a Func, the last generic parameter is always the return type. You should also note that we can now return a value from our anonymous method.

A Two-Parameter Func

Finally let’s take a look at an example of a multi-parameter Func. Once again we’ll use the same example, except this time rather than passing the Article object to the anonymous method we’ll pass only the data it needs: the date created and the title:

   1: public void IterateArticles( Func<DateTime,string,string> process, List<Article> articles )
   2: {
   3:     foreach( Article article in articles )
   4:         article.Title = process( article.Created, article.Title );
   5: }
   6:
   7: public void ProcessArticles()
   8: {
   9:     List<Article> articles = GetArticles();
  10:     IterateArticles( (x,y)=>{
  11:
  12:         if( x.Year < 2008 )
  13:             return y.ToLower();
  14:         else
  15:             return y.ToUpper();
  16:
  17:     }, articles );
  18: }

Here we have specified three generic parameters for the Func; two to be passed in, one to return. In our lambda expression we have also had to wrap the “x,y” in parentheses to prevent the compiler from throwing an error because it thinks the comma is intended to separate parameters to the IterateArticles method rather than the Func.

A Zero-Parameter Func

Occasionally you may need to use a Func that will not require any parameters. Trying to pass this into the lambda consumer using only the “{…}” notation of anonymous methods will not work, you will need to use “()=>{…}” as seen below:

   1: public void IterateArticles( Func<string> process, List<Article> articles )
   2: {
   3:     foreach( Article article in articles )
   4:         article.Title = process();
   5: }
   6: public void ProcessArticles()
   7: {
   8:     List<Article> articles = GetArticles();
   9:     IterateArticles( ()=>{
  10:
  11:         return "Published on " + DateTime.Now.ToString();
  12:
  13:     }, articles );
  14: }

I hope this helps get you started creating your own lambda consuming methods. Keep in mind that Funcs and Actions are not limited to just one or two parameters as shown in this article; both can accept up to four input parameters (not including the Func’s extra return parameter). As you become more comfortable creating lambda expressions and lambda consumers I am certain you will find many places in our your code that can be simplified and/or made more flexible by using them.

Kick It on DotNetKicks.comShout It on DotNetShoutOuts.com