Created
February 9, 2016 21:27
-
-
Save Porges/976446e869d1b77e11d0 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
using System; | |
using System.Runtime.CompilerServices; | |
namespace BadIdeas | |
{ | |
using static Props; // Boo, C#, why does this need to be located here? | |
interface IReadable<TName, out T> { } | |
interface IWritable<TName, in T> { } | |
interface IProperty<TName, T> | |
: IReadable<TName, T> | |
, IWritable<TName, T> | |
{ } | |
static class Extensions | |
{ | |
private static class Dict<T> | |
where T : class | |
{ | |
public static readonly ConditionalWeakTable<object, T> Instance = | |
new ConditionalWeakTable<object, T>(); | |
} | |
// need a consistent reference type to store in ConditionalWeakTable | |
private sealed class Stored<TName, TType> | |
{ | |
public TType Value; | |
} | |
// These should really take a generic `T : class, IWritable` to prevent | |
// passing a struct to it.... | |
public static void Set<T, TName, TType>( | |
this T writable, | |
Prop<TName, TType> prop, | |
TType value) | |
where T | |
: class | |
, IWritable<TName, TType> | |
=> Set(writable, value); | |
public static void Set<T, TName, TType, TName2, TType2>( | |
this T writable, | |
Prop<TName, TType> prop, | |
TType value, | |
Prop<TName2, TType2> prop2, | |
TType2 value2) | |
where T | |
: class | |
, IWritable<TName, TType> | |
, IWritable<TName2, TType2> | |
{ | |
Set<TName, TType>(writable, value); | |
Set<TName2, TType2>(writable, value2); | |
} | |
public static TType Get<T, TName, TType>( | |
this T readable, | |
Prop<TName, TType> prop) | |
where T | |
: class | |
, IReadable<TName, TType> | |
=> Get(readable); | |
public static Tuple<TType, TType2> Get<T, TName, TType, TName2, TType2>( | |
this T readable, | |
Prop<TName, TType> prop, | |
Prop<TName2, TType2> prop2) | |
where T | |
: class | |
, IReadable<TName, TType> | |
, IReadable<TName2, TType2> | |
=> Tuple.Create( | |
Get<TName, TType>(readable), | |
Get<TName2, TType2>(readable)); | |
// actual implementation of Get/Set | |
// this could be pushed into the Prop class (and then there could be | |
// different types of Prop). | |
private static TType Get<TName, TType>(IReadable<TName, TType> readable) | |
=> Dict<Stored<TName, TType>>.Instance.GetOrCreateValue(readable).Value; | |
private static void Set<TName, TType>(IWritable<TName, TType> o, TType value) | |
=> Dict<Stored<TName, TType>>.Instance.GetOrCreateValue(o).Value = value; | |
public static Const<T> AsConst<T>(this T it) => it; | |
public static TType Get<T, TName, TType>(this Const<T> readable, Prop<TName, TType> prop) | |
where T : class, IReadable<TName, TType> | |
=> readable.Value.Get(prop); | |
} | |
public struct Const<T> | |
{ | |
internal readonly T Value; // not exposed | |
private Const(T value) | |
{ | |
Value = value; | |
} | |
public static implicit operator Const<T>(T value) => new Const<T>(value); | |
} | |
abstract class Prop<TName, TType> { } | |
static class Props | |
{ | |
public class HeightProp : Prop<HeightProp, long> { } | |
public class WidthProp : Prop<WidthProp, long> { } | |
public class ColorProp : Prop<ColorProp, Colors> { } | |
// always null, we dispatch on the type | |
public static HeightProp Height { get; } | |
public static WidthProp Width { get; } | |
public static ColorProp Color { get; } | |
} | |
enum Colors { Black, Red } | |
class Rectangle | |
: IProperty<HeightProp, long> | |
, IProperty<WidthProp, long> | |
{ } | |
class ColorRectangle | |
: Rectangle | |
, IProperty<ColorProp, Colors> | |
{ } | |
class Program | |
{ | |
static long Area<T>(Const<T> shape) | |
where T | |
: class | |
, IReadable<HeightProp, long> | |
, IReadable<WidthProp, long> | |
=> shape.Get(Height) * shape.Get(Width); | |
static void Main(string[] args) | |
{ | |
var rec = new ColorRectangle(); | |
rec.Set(Height, 2); | |
// multi-set | |
rec.Set( | |
Height, 3, | |
Width, 4); | |
rec.Set(Color, Colors.Red); | |
var frozenRec = rec.AsConst(); | |
// frozenRec is simply a const reference, not a clone | |
// (as in C++), so this does not compile: | |
// frozenRec.Set(Props.Height, 3); | |
// but we can mutate the underlying value | |
rec.Set(Height, 5); | |
Console.WriteLine(Area(rec.AsConst())); // unfortunately implicit conversion doesn't help | |
// multi-get | |
Console.WriteLine($"Area {rec.Get(Height, Width)} = {Area(frozenRec)}"); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment