Skip to content

Instantly share code, notes, and snippets.

@jcdickinson
Created January 25, 2012 15:05
Show Gist options
  • Save jcdickinson/1676672 to your computer and use it in GitHub Desktop.
Save jcdickinson/1676672 to your computer and use it in GitHub Desktop.
Interface Delegate Methods/Mixins
class Program
{
static void Main(string[] args)
{
var ge = new GameEntity(
me =>
{
// WHOAH! We can store some private data for the mixin.
// Starting to look like javascript though :(.
var tt = 0f;
return dt => Console.WriteLine("AI Total Time = {0}", tt += dt);
},
me =>
{
var tt = 0f;
return dt => Console.WriteLine("Physics Total Time = {0}", tt += dt);
}
);
ge.AIRoutine(33);
ge.PhysicsRoutine(33);
ge.PhysicsRoutine(33);
Console.ReadLine();
}
}
public delegate void TimedFunc(float dt);
class GameEntity : IGameEntity
{
public TimedFunc AIRoutine
{
get;
private set;
}
public TimedFunc PhysicsRoutine
{
get;
private set;
}
public GameEntity(Func<IGameEntity, TimedFunc> ai, Func<IGameEntity, TimedFunc> physics)
{
AIRoutine = ai(this);
PhysicsRoutine = physics(this);
}
}
interface IGameEntity
{
TimedFunc AIRoutine { get; }
TimedFunc PhysicsRoutine { get; }
}
class Program
{
static void Main(string[] args)
{
var ge = new GameEntity(
(t, dt) => Console.WriteLine("AI!"),
(t, dt) => Console.WriteLine("Physics!")
);
ge.AIRoutine(100);
ge.PhysicsRoutine(200);
Console.ReadLine();
}
}
public delegate void TimedFunc<TThis>(TThis thisObject, float dt);
public delegate void TimedFunc(float dt);
class GameEntity : IGameEntity
{
public TimedFunc AIRoutine
{
get;
private set;
}
public TimedFunc PhysicsRoutine
{
get;
private set;
}
public GameEntity(TimedFunc<IGameEntity> ai, TimedFunc<IGameEntity> physics)
{
AIRoutine = this.ProvideTo<IGameEntity, TimedFunc<IGameEntity>, TimedFunc>(ai);
PhysicsRoutine = this.ProvideTo<IGameEntity, TimedFunc<IGameEntity>, TimedFunc>(physics);
}
}
interface IGameEntity
{
TimedFunc AIRoutine { get; }
TimedFunc PhysicsRoutine { get; }
}
static class ExpressionExtensions
{
public static TDelegateOut ProvideTo<TThis, TDelegateIn, TDelegateOut>(this TThis thisObject, TDelegateIn del)
{
if ((thisObject == null).OnlyIfDebug())
throw new ArgumentNullException("thisObject");
if ((del == null).OnlyIfDebug())
throw new ArgumentNullException("del");
var outType = typeof(TDelegateOut);
var inType = typeof(TDelegateIn);
var outInvoke = outType.GetMethod("Invoke", BindingFlags.Public | BindingFlags.Instance);
var inInvoke = inType.GetMethod("Invoke", BindingFlags.Public | BindingFlags.Instance);
// Check that both are delegates and that the return types match.
if ((outInvoke == null || outInvoke.ReturnType != inInvoke.ReturnType).OnlyIfDebug())
throw new ArgumentOutOfRangeException("TDelegateOut");
if ((inInvoke == null).OnlyIfDebug())
throw new ArgumentOutOfRangeException("TDelegateIn");
var outParams = outInvoke.GetParameters();
var inParams = inInvoke.GetParameters();
// Check that the parameters make sense.
if ((outParams.Length != inParams.Length - 1).OnlyIfDebug())
throw new ArgumentOutOfRangeException("TDelegateOut");
// Parameters for the return delegate.
var paramsExprs = new ParameterExpression[outParams.Length];
// Arguments for the method call inside the expression.
var args = new Expression[inParams.Length];
args[0] = Expression.Constant(thisObject);
for (var i = 0; i < outParams.Length; i++)
{
var op = outParams[i];
var ip = inParams[i + 1];
if ((op.ParameterType != ip.ParameterType).OnlyIfDebug())
throw new ArgumentOutOfRangeException(string.Format("TDelegateOut({0})", op.Name));
paramsExprs[i] = Expression.Parameter(op.ParameterType, op.Name);
args[i + 1] = paramsExprs[i];
}
var call = Expression.Invoke(Expression.Constant(del), args);
var lambda = Expression.Lambda<TDelegateOut>(call, paramsExprs);
return lambda.Compile();
}
private static bool OnlyIfDebug(this bool value)
{
#if DEBUG
return value;
#else
return false;
#endif
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment