Skip to content

Instantly share code, notes, and snippets.

@paulodiogo
Forked from Virtlink/TypeSwitch.cs
Last active August 29, 2015 14:16
Show Gist options
  • Save paulodiogo/de18e028cb9bdedf405b to your computer and use it in GitHub Desktop.
Save paulodiogo/de18e028cb9bdedf405b to your computer and use it in GitHub Desktop.
using System;
namespace Virtlink
{
/// <summary>
/// Executes a particular piece of code based on the type of the argument.
/// </summary>
/// <example>
/// Usage example:
/// <code>
/// public string GetName(object value)
/// {
/// string name = null;
/// TypeSwitch.On(operand)
/// .Case((C x) => name = x.FullName)
/// .Case((B x) => name = x.LongName)
/// .Case((A x) => name = x.Name)
/// .Case((X x) => name = x.ToString(CultureInfo.CurrentCulture))
/// .Case((Y x) => name = x.GetIdentifier())
/// .Default((x) => name = x.ToString());
/// return name;
/// }
/// </code>
/// </example>
/// <remarks>
/// Created by Virtlink. Original source code on GitHub:
/// <see href="https://gist.github.com/Virtlink/8722649"/>.
/// </remarks>
public static class TypeSwitch
{
/// <summary>
/// Executes a particular piece of code based on the type of the argument.
/// </summary>
/// <typeparam name="TSource">The argument's type.</typeparam>
/// <param name="value">The switch argument.</param>
/// <returns>An object on which the switch cases can be specified.</returns>
public static Switch<TSource> On<TSource>(TSource value)
{
return new Switch<TSource>(value);
}
/// <summary>
/// Internal class used by the <see cref="TypeSwitch"/> static class.
/// </summary>
/// <typeparam name="TSource">The source type.</typeparam>
public sealed class Switch<TSource>
{
/// <summary>
/// The source value.
/// </summary>
private readonly TSource value;
/// <summary>
/// Whether a switch case handled the value.
/// </summary>
private bool handled = false;
/// <summary>
/// Initializes a new instance
/// of the <see cref="Switch{TSource}"/> class.
/// </summary>
/// <param name="value">The switch value.</param>
internal Switch(TSource value)
{
this.value = value;
}
/// <summary>
/// Executes the specified piece of code when the type
/// of the argument is assignable to the specified type.
/// </summary>
/// <typeparam name="TTarget">The target type.</typeparam>
/// <param name="action">The action to execute.</param>
/// <returns>An object on which further switch cases
/// can be specified.</returns>
public Switch<TSource> Case<TTarget>(Action action)
where TTarget : TSource
{
if (action == null)
throw new ArgumentNullException("action");
return Case<TTarget>(_ => action());
}
/// <summary>
/// Executes the specified piece of code when the type
/// of theargument is assignable to the specified type.
/// </summary>
/// <typeparam name="TTarget">The target type.</typeparam>
/// <param name="action">The action to execute.</param>
/// <returns>An object on which further switch cases
/// can be specified.</returns>
public Switch<TSource> Case<TTarget>(Action<TTarget> action)
where TTarget : TSource
{
if (action == null)
throw new ArgumentNullException("action");
if (!this.handled && this.value is TTarget)
{
action((TTarget) this.value);
this.handled = true;
}
return this;
}
/// <summary>
/// Executes the specified piece of code when none of the other
/// cases handles the specified type.
/// </summary>
/// <param name="action">The action to execute.</param>
public void Default(Action action)
{
if (action == null)
throw new ArgumentNullException("action");
Default(_ => action());
}
/// <summary>
/// Executes the specified piece of code when none of the other
/// cases handles the specified type.
/// </summary>
/// <param name="action">The action to execute.</param>
public void Default(Action<TSource> action)
{
if (action == null)
throw new ArgumentNullException("action");
if (!this.handled)
action(this.value);
}
}
}
}
using NUnit.Framework;
namespace Virtlink
{
/// <summary>
/// Tests for the <see cref="TypeSwitch"/> class.
/// </summary>
/// <remarks>
/// Created by Virtlink. Original source code on GitHub:
/// <see href="https://gist.github.com/Virtlink/8722649"/>.
/// </remarks>
[TestFixture]
public class TypeSwitchTests
{
interface I { string GetID(); }
interface J { string ShortName { get; } }
class A { public string Name { get { return "A"; } } }
class B : A { public string LongName { get { return "B"; } } }
class C : B, I, J { public string FullName { get { return "C"; } } public string GetID() { return "CI"; } public string ShortName { get { return "CJ"; } } }
[Test]
public void BaseClassObject()
{
// Arrange
object value = new A();
string name = null;
// Act
TypeSwitch.On(value)
.Case((C x) => name = x.FullName)
.Case((B x) => name = x.LongName)
.Case<A>(() => name = "A")
.Default((x) => name = x.ToString());
// Assert
Assert.AreEqual("A", name);
}
[Test]
public void BaseClass()
{
// Arrange
A value = new A();
string name = null;
// Act
TypeSwitch.On(value)
.Case((C x) => name = x.FullName)
.Case((B x) => name = x.LongName)
.Case<A>(() => name = "A")
.Default((x) => name = x.ToString());
// Assert
Assert.AreEqual("A", name);
}
[Test]
public void DerivedClassObject()
{
// Arrange
object value = new C();
string name = null;
// Act
TypeSwitch.On(value)
.Case((C x) => name = x.FullName)
.Case((B x) => name = x.LongName)
.Case<A>(() => name = "A")
.Default((x) => name = x.ToString());
// Assert
Assert.AreEqual("C", name);
}
[Test]
public void DerivedClassAsBaseClass()
{
// Arrange
A value = new C();
string name = null;
// Act
TypeSwitch.On(value)
.Case((C x) => name = x.FullName)
.Case((B x) => name = x.LongName)
.Case<A>(() => name = "A")
.Default((x) => name = x.ToString());
// Assert
Assert.AreEqual("C", name);
}
[Test]
public void DerivedClassCaseWrongOrder()
{
// Arrange
object value = new C();
string name = null;
// Act
TypeSwitch.On(value)
.Case((B x) => name = x.LongName)
.Case((C x) => name = x.FullName)
.Case<A>(() => name = "A")
.Default((x) => name = x.ToString());
// Assert
Assert.AreEqual("B", name);
}
[Test]
public void InterfaceObject()
{
// Arrange
object value = new C();
string name = null;
// Act
TypeSwitch.On(value)
.Case((I x) => name = x.GetID())
.Case((J x) => name = x.ShortName)
.Case((C x) => name = x.FullName)
.Case<A>(() => name = "A")
.Default((x) => name = x.ToString());
// Assert
Assert.AreEqual("CI", name);
}
[Test]
public void Interface()
{
// Arrange
J value = new C();
string name = null;
// Act
TypeSwitch.On(value)
.Case((J x) => name = x.ShortName)
.Case((C x) => name = x.FullName)
.Default((x) => name = x.ToString());
// Assert
Assert.AreEqual("CJ", name);
}
[Test]
public void Default()
{
// Arrange
var value = new object();
string name = null;
// Act
TypeSwitch.On(value)
.Case((J x) => name = x.ShortName)
.Case((C x) => name = x.FullName)
.Default((x) => name = x.ToString());
// Assert
Assert.AreEqual("System.Object", name);
}
[Test]
public void DefaultNoArg()
{
// Arrange
var value = new object();
string name = null;
// Act
TypeSwitch.On(value)
.Case((J x) => name = x.ShortName)
.Case((C x) => name = x.FullName)
.Default(() => name = "Default");
// Assert
Assert.AreEqual("Default", name);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment