Skip to content

Instantly share code, notes, and snippets.

@Porges
Created February 9, 2016 21:27
Show Gist options
  • Save Porges/976446e869d1b77e11d0 to your computer and use it in GitHub Desktop.
Save Porges/976446e869d1b77e11d0 to your computer and use it in GitHub Desktop.
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