Created
December 9, 2014 10:40
-
-
Save makomweb/fee323096ce7b8aab5a9 to your computer and use it in GitHub Desktop.
Typeswitch play ground
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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