Skip to content

Instantly share code, notes, and snippets.

@makomweb
Created December 9, 2014 10:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save makomweb/fee323096ce7b8aab5a9 to your computer and use it in GitHub Desktop.
Save makomweb/fee323096ce7b8aab5a9 to your computer and use it in GitHub Desktop.
Typeswitch play ground
namespace VisitorTests
{
public interface INt
{
}
public interface INtel
{
}
public abstract class NT : INt
{
}
public class NTList : NT
{
}
public class NTTask : NT, INtel
{
}
namespace MyTests1 // This is the most naive approach, because it's fragile. It also has the largest complexity (!) because the IfType function is called for every single type which is checked.
{
public static class TypeHelper
{
public static object IfType<T>(this object instance, Action<T> action) where T : class
{
if (instance is T)
{
action(instance as T);
return null;
}
return instance;
}
}
public class Tests
{
[Test]
public void Run()
{
var nt1 = new NTList();
var nt2 = new NTTask();
var type = "";
nt1.IfType<INtel>(_ => { type = "INtel"; })
.IfType<INt>(_ => { type = "INt"; })
.IfType<NTList>(_ => { type = "list"; })
.IfType<NTTask>(_ => { type = "task"; });
type.Should().Be("list"); // ASSERTION fails because you require the programmer to respect the class hierarchy when winding up the .IfType cascade! --> Most derived types have to come last, interfaces have to come first, implementing many interfaces can lead to confusion!
nt2.IfType<NTList>(_ => { type = "list"; })
.IfType<NTTask>(_ => { type = "task"; });
type.Should().Be("task");
}
}
}
namespace MyTests2 // This is the simplest consistent approach. It is less generic, but with the modest complexity (!), because there is no call chaining to achieve type lookup!
{
public static class PrintHelper
{
private static readonly Dictionary<Type, int> Map = new Dictionary<Type, int>
{
{typeof (INt), 1},
{typeof (INtel), 2},
{typeof (NT), 3},
{typeof (NTList), 4},
{typeof (NTTask), 5}
};
public static void HelpPrint(this INt instance, Action<string> print)
{
var t = Map[instance.GetType()];
switch (t)
{
case 1:
print("INt");
break;
case 2:
print("INtel");
break;
case 3:
print("NT");
break;
case 4:
print("NTList");
break;
case 5:
print("NTTask");
break;
default:
throw new NotSupportedException("Instance has an unsupported type: " + instance.GetType());
}
}
}
public class Tests
{
[Test]
public void Run()
{
var nt1 = new NTList();
var nt2 = new NTTask();
var type = "";
nt1.HelpPrint(x => { type = x; });
type.Should().Be("NTList");
nt2.HelpPrint(x => { type = x; });
type.Should().Be("NTTask");
}
}
}
namespace MyTests3 // Similar to MyTest1, but less fragile because of the MatchWinType.
{
public class TypeSwitch
{
private readonly MatchWinType _win;
public enum MatchWinType { FirstMatchWins, LastMatchWins }
public TypeSwitch(Object o, MatchWinType w = MatchWinType.FirstMatchWins)
{
Object = o;
_win = w;
}
public Object Object { get; private set; }
public bool LastMatchWins { get { return _win == MatchWinType.LastMatchWins; }}
}
public static class TypeSwitchExtensions
{
public static TypeSwitch Case<T>(this TypeSwitch s, Action<T> a)
where T : class
{
if (s == null)
return null;
var t = s.Object as T;
if (t == null)
return s;
a(t);
return s.LastMatchWins ? s : null;
}
}
public class Tests
{
[Test]
public void Run()
{
var nt1 = new NTList();
var nt2 = new NTTask();
var type = "";
new TypeSwitch(nt1)
.Case<NTList>(_ => { type = "ntlist"; })
.Case<NTTask>(_ => { type = "nttask"; })
.Case<INtel>(_ => { type = "intel"; })
.Case<INt>(_ => { type = "int"; })
.Case<NT>(_ => { type = "nt"; });
type.Should().Be("ntlist");
new TypeSwitch(nt2, TypeSwitch.MatchWinType.LastMatchWins)
.Case<INtel>(_ => { type = "intel"; })
.Case<INt>(_ => { type = "int"; })
.Case<NT>(_ => { type = "nt"; })
.Case<NTList>(_ => { type = "ntlist"; })
.Case<NTTask>(_ => { type = "nttask"; });
type.Should().Be("nttask");
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment