Chapter 20 - Dynamic Programming
Numeric type unification
static dynamic Mean (dynamic x, dynamic y) => (x + y) / 2;
void Main()
{
int x = 3, y = 5;
Console.WriteLine (Mean (x, y));
}
Numeric type unification - typesafe
static T Mean<T> (T x, T y)
{
dynamic result = ((dynamic) x + y) / 2;
return (T) result;
}
void Main()
{
int x = 3, y = 5;
Console.WriteLine (Mean (x, y));
}
Numeric type unification - with static overloads
static T Mean<T> (T x, T y)
{
"Dynamic".Dump();
dynamic result = ((dynamic) x + y) / 2;
return (T) result;
}
static double Mean (double x, double y)
{
"Static".Dump();
return (x + y) / 2;
}
void Main()
{
Mean (3, 4).Dump();
Mean (3.0, 4.0).Dump();
}
Visitor pattern
class ToXElementPersonVisitor
{
public XElement DynamicVisit (Person p)
{
return Visit ((dynamic)p);
}
XElement Visit (Person p)
{
return new XElement ("Person",
new XAttribute ("Type", p.GetType().Name),
new XElement ("FirstName", p.FirstName),
new XElement ("LastName", p.LastName),
p.Friends.Select (f => DynamicVisit (f))
);
}
XElement Visit (Customer c) // Specialized logic for customers
{
XElement xe = Visit ((Person)c); // Call "base" method
xe.Add (new XElement ("CreditLimit", c.CreditLimit));
return xe;
}
XElement Visit (Employee e) // Specialized logic for employees
{
XElement xe = Visit ((Person)e); // Call "base" method
xe.Add (new XElement ("Salary", e.Salary));
return xe;
}
}
void Main()
{
var cust = new Customer { FirstName = "Joe", LastName = "Bloggs", CreditLimit = 123 };
cust.Friends.Add (new Employee { FirstName = "Sue", LastName = "Brown", Salary = 50000 });
new ToXElementPersonVisitor().DynamicVisit (cust).Dump();
}
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
// The Friends collection may contain Customers & Employees:
public readonly IList<Person> Friends = new Collection<Person> ();
}
class Customer : Person { public decimal CreditLimit { get; set; } }
class Employee : Person { public decimal Salary { get; set; } }
Visitor pattern - with abstract base class
abstract class PersonVisitor<T>
{
public T DynamicVisit (Person p) { return Visit ((dynamic)p); }
protected abstract T Visit (Person p);
protected virtual T Visit (Customer c) { return Visit ((Person) c); }
protected virtual T Visit (Employee e) { return Visit ((Person) e); }
}
class ToXElementPersonVisitor : PersonVisitor<XElement>
{
protected override XElement Visit (Person p)
{
return new XElement ("Person",
new XAttribute ("Type", p.GetType().Name),
new XElement ("FirstName", p.FirstName),
new XElement ("LastName", p.LastName),
p.Friends.Select (f => DynamicVisit (f))
);
}
protected override XElement Visit (Customer c)
{
XElement xe = base.Visit (c);
xe.Add (new XElement ("CreditLimit", c.CreditLimit));
return xe;
}
protected override XElement Visit (Employee e)
{
XElement xe = base.Visit (e);
xe.Add (new XElement ("Salary", e.Salary));
return xe;
}
}
void Main()
{
var cust = new Customer { FirstName = "Joe", LastName = "Bloggs", CreditLimit = 123 };
cust.Friends.Add (new Employee { FirstName = "Sue", LastName = "Brown", Salary = 50000 });
new ToXElementPersonVisitor().DynamicVisit (cust).Dump();
}
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
// The Friends collection may contain Customers & Employees:
public readonly IList<Person> Friends = new Collection<Person> ();
}
class Customer : Person { public decimal CreditLimit { get; set; } }
class Employee : Person { public decimal Salary { get; set; } }
Anonymously calling members of generic type
public class Foo<T> { public T Value; }
void Write (dynamic obj)
{
try
{
Console.WriteLine (obj.Value);
}
catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException)
{
"Member doesn't exist".Dump();
}
}
void Main()
{
Write (new Foo<int> { Value = 123 });
Write (new Foo<string> { Value = "foo" });
}
Anonymously calling members of generic type with multiple dispatch
public class Foo<T> { public T Value; }
void Write (dynamic obj)
{
object result = GetFooValue (obj);
if (result != null) Console.WriteLine (result);
}
static T GetFooValue<T> (Foo<T> foo) { return foo.Value; }
static object GetFooValue (object foo) { return null; }
void Main()
{
Write (new Foo<int> { Value = 123 });
Write (new Foo<string> { Value = "foo" });
}
Anonymously calling members of generic type - IGrouping
string GetGroupKey<TKey,TElement> (IGrouping<TKey,TElement> group)
{
return "Group with key=" + group.Key + ": ";
}
string GetGroupKey (object source) { return null; }
string ToStringEx (object value)
{
if (value == null) return "<null>";
if (value is string) return (string) value;
if (value.GetType().IsPrimitive) return value.ToString();
StringBuilder sb = new StringBuilder();
string groupKey = GetGroupKey ((dynamic)value); // Dynamic dispatch
if (groupKey != null) sb.Append (groupKey);
if (value is IEnumerable)
foreach (object element in ((IEnumerable)value))
sb.Append (ToStringEx (element) + " ");
if (sb.Length == 0) sb.Append (value.ToString());
return "\r\n" + sb.ToString();
}
void Main()
{
ToStringEx ("xyyzzz".GroupBy (c => c)).Dump();
}
DynamicObject - dynamic XAttributes
static class XExtensions
{
public static dynamic DynamicAttributes (this XElement e)
{
return new XWrapper (e);
}
class XWrapper : DynamicObject
{
XElement _element;
public XWrapper (XElement e) { _element = e; }
public override bool TryGetMember (GetMemberBinder binder,
out object result)
{
result = _element.Attribute (binder.Name).Value;
return true;
}
public override bool TrySetMember (SetMemberBinder binder,
object value)
{
_element.SetAttributeValue (binder.Name, value);
return true;
}
}
}
void Main()
{
XElement x = XElement.Parse (@"<Label Text=""Hello"" Id=""5""/>");
dynamic da = x.DynamicAttributes();
Console.WriteLine (da.Id); // 5
da.Text = "Foo";
Console.WriteLine (x.ToString()); // <Label Text="Foo" Id="5" />
}
DynamicObject - dynamic DataReader
void Main()
{
var command = new SqlCommand ("select top 3 * from Purchase", (SqlConnection) Connection);
Connection.Open();
using (IDataReader reader = command.ExecuteReader (CommandBehavior.CloseConnection))
{
dynamic dr = new DynamicReader (reader);
while (reader.Read())
{
int id = dr.ID;
string description = dr.Description;
DateTime date = dr.Date;
decimal price = dr.Price;
new { description, date, price }.Dump();
}
}
}
public class DynamicReader : DynamicObject
{
readonly IDataRecord _dataRecord;
public DynamicReader (IDataRecord dr) { _dataRecord = dr; }
public override bool TryGetMember (GetMemberBinder binder,
out object result)
{
result = _dataRecord [binder.Name];
return true;
}
}
DynamicObject - TryBinaryOperation and TryInvoke
static void Main()
{
dynamic d = new Duck();
Console.WriteLine (d + d); // foo
Console.WriteLine (d (78, 'x')); // 123
}
public class Duck : DynamicObject
{
public override bool TryBinaryOperation (BinaryOperationBinder binder,
object arg, out object result)
{
Console.WriteLine (binder.Operation); // Add
result = "foo";
return true;
}
public override bool TryInvoke (InvokeBinder binder,
object[] args, out object result)
{
Console.WriteLine (args[0]); // 78
result = 123;
return true;
}
}
ExpandoObject
dynamic x = new ExpandoObject();
x.FavoriteColor = ConsoleColor.Green;
x.FavoriteNumber = 7;
Console.WriteLine (x.FavoriteColor); // Green
Console.WriteLine (x.FavoriteNumber); // 7
var dict = (IDictionary<string,object>) x;
Console.WriteLine (dict ["FavoriteColor"]); // Green
Console.WriteLine (dict ["FavoriteNumber"]); // 7
Console.WriteLine (dict.Count); // 2
Python calculator
static void Main()
{
int result = (int) Calculate ("2 * 3");
Console.WriteLine (result); // 6
var list = (IEnumerable) Calculate ("[1, 2, 3] + [4, 5]");
foreach (int n in list) Console.Write (n); // 12345
}
static object Calculate (string expression)
{
ScriptEngine engine = Python.CreateEngine();
return engine.Execute (expression);
}
Python interop - passing state
// The following string could come from a file or database:
string auditRule = "taxPaidLastYear / taxPaidThisYear > 2";
ScriptEngine engine = Python.CreateEngine ();
ScriptScope scope = engine.CreateScope ();
scope.SetVariable ("taxPaidLastYear", 20000m);
scope.SetVariable ("taxPaidThisYear", 8000m);
ScriptSource source = engine.CreateScriptSourceFromString (auditRule, SourceCodeKind.Expression);
bool auditRequired = (bool) source.Execute (scope);
Console.WriteLine (auditRequired); // True
Python interop - returning variables
string code = "result = input * 3";
ScriptEngine engine = Python.CreateEngine();
ScriptScope scope = engine.CreateScope();
scope.SetVariable ("input", 2);
ScriptSource source = engine.CreateScriptSourceFromString (code, SourceCodeKind.SingleStatement);
source.Execute (scope);
Console.WriteLine (scope.GetVariable ("result")); // 6
Python interop - type marshaling
string code = @"sb.Append (""World"")";
ScriptEngine engine = Python.CreateEngine ();
ScriptScope scope = engine.CreateScope ();
var sb = new StringBuilder ("Hello");
scope.SetVariable ("sb", sb);
ScriptSource source = engine.CreateScriptSourceFromString (code, SourceCodeKind.SingleStatement);
source.Execute (scope);
string s = sb.ToString();
s.Dump ("StringBuilder");