A better switch with Lambdas in .NET, Part 2 (A StringComparison implementation)

By | September 12, 2013

In my previous post, I have implemented one simple Lambda based switch case which complement the existing switch cases with some additional support of other objects that implements Equals operator in their code. But the implementation that I have provided wouldn’t made you much sense to add in your project or if you are really a fan of lambdas you might have already added that in your code.

Today, I am going to show how you could extend the existing code a little bit further to add more stuffs to it. Let us take back to the example that I have provided in the previous example.

public static class SimpleSwitch
{
public class CaseInfo
{
public T Case { get; set; }
public Action TargetAction { get; set; }
public bool IsDefault { get; set; }
}

public static void Me(T source, params CaseInfo[] cases)
{
CaseInfo defaultCase = null;
bool isTargetActionTaken = false;
foreach (var entry in cases)
{
if (entry.IsDefault)
defaultCase = entry;

if (!entry.IsDefault && entry.Case.Equals(source))
{
entry.TargetAction(source);
isTargetActionTaken = true;
break;
}
}
if (!isTargetActionTaken && defaultCase != null)
defaultCase.TargetAction(source);

}
public static CaseInfo Case(T source, Action action)
{
return new CaseInfo { TargetAction = x => action(), Case = source };
}
public static CaseInfo Default(Action action)
{
return new CaseInfo { TargetAction = x => action(), IsDefault = true };
}
}

This code is simple, yet powerful when dealing with Types that implements Equals operator, as under the hood, it uses the Equals overloaded operator to determine the equality of the type and the cases. If the Equals operator isnt overloaded for the type for which the case is used, it will use ReferenceEquals behind the scene. To demonstrate this let us take an example :

public class MyType
{
public string Id { get; set; }
public MyType()
{
this.Id = Guid.NewGuid().ToString();
}

}
static void Main(string[] args)
{
MyType typeSource = new MyType();
SimpleSwitch.Me(typeSource,
new SimpleSwitch.CaseInfo
{
Case = typeSource,
TargetAction = e =>
{
Console.WriteLine(e.Id);
}
},

new SimpleSwitch.CaseInfo
{
IsDefault = true,
TargetAction = e =>
{
Console.WriteLine("Default:" + e.Id);
}
});

Console.ReadKey(true);
}

Now if you compile the call here, you will see the ReferenceEquals is used and the typeSource is identified for the case. I have used Id to generate a Guid inside the Id Field, such that when the id gets printed, you can easily identify it as to be the same object.

Lets show some more power: 

Thats it all with the previous example, let us now take a look at adding something more to the Switcher. We always encounter a situation where we need to compare between two strings but we want to ignore case. There is an enum already present in the library which identifies the comparison behavior of the strings, we can make use of it and create a Switch /case like this. The implementation will look like :

public static class ComparerSwitch
{
public class CaseInfo
{
public string Case { get; set; }
public Action TargetAction { get; set; }
public bool IsDefault { get; set; }
}

public static void Me(string source, StringComparison comparisonType, params CaseInfo[] cases)
{
foreach (var entry in cases)
{
if (comparisonType == StringComparison.Ordinal)
{
if (source == entry.Case) // This works because string is interned
{
entry.TargetAction(source);
break;
}
}
else if (comparisonType == StringComparison.OrdinalIgnoreCase)
{
if (source.ToUpper() == entry.Case.ToUpper())// This works because string is interned
{
entry.TargetAction(source);
break;
}

}
else
{
int isEqual = 0;
switch (comparisonType)
{
case StringComparison.CurrentCulture:
isEqual = CultureInfo.CurrentCulture.CompareInfo.Compare(source, entry.Case, CompareOptions.None);
break;
case StringComparison.CurrentCultureIgnoreCase:
isEqual = CultureInfo.CurrentCulture.CompareInfo.Compare(source, entry.Case, CompareOptions.IgnoreCase);
break;
case StringComparison.InvariantCulture:
isEqual = CultureInfo.InvariantCulture.CompareInfo.Compare(source, entry.Case, CompareOptions.None);
break;
case StringComparison.InvariantCultureIgnoreCase:
isEqual = CultureInfo.InvariantCulture.CompareInfo.Compare(source, entry.Case, CompareOptions.IgnoreCase);
break;
}
if (isEqual == 0)
{
entry.TargetAction(source); break;
}
}
}

}

public static CaseInfo Case(string source, Action action)
{
return new CaseInfo { TargetAction = x => action(), Case = source };
}
public static CaseInfo Case(string source, Action action)
{
return new CaseInfo { TargetAction = action, Case = source };
}

public static CaseInfo Default(Action action)
{
return new CaseInfo { TargetAction = x => action(), IsDefault = true };
}
}

Here in the code above, you can see the StringComparison enum is used to determine the behavior of the comparison, where we can specify the different types of comparisons available in the .NET.  Now to call this, let us take an example :

static void Main(string[] args)
{
string strSource = "Abhishek";
ComparerSwitch.Me(strSource,
StringComparison.InvariantCultureIgnoreCase,
new ComparerSwitch.CaseInfo
{
Case = "ABHISHEK",
TargetAction = e =>
{
Console.WriteLine("ABHISHEK:" + e);
}
},
new ComparerSwitch.CaseInfo
{
Case = "abhishek",
TargetAction = e =>
{
Console.WriteLine("abhishek:" + e);
}
},
new ComparerSwitch.CaseInfo
{
Case = "Abhishek",
TargetAction = e =>
{
Console.WriteLine("Abhishek:" + e);
}
},
new ComparerSwitch.CaseInfo
{
IsDefault = true,
TargetAction = e =>
{
Console.WriteLine("Default:" + e);
}
});

Console.ReadKey(true);
}

Here you can see the string “Abhishek” and “ABHISHEK” are quite different, but when the InvariantCultureIgnoreCase is used, the first case is selected.

Conclusion

I hope you found this helpful, I will be keep on adding more things to it on the series. Stay tune to see more.

Hope you like it.

One thought on “A better switch with Lambdas in .NET, Part 2 (A StringComparison implementation)

  1. Pingback: A better switch with Lambdas in .NET, Part 3 (Adding a IComparer) | Daily .NET Tips

Comments are closed.