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");