tsunami
log in
email
password
links
newest items
tag list
syntax reference
tag:tsunami
history
C# Code Standards
Christianity: NT says read OT
Luke's recommended book list
tsunami technology
Predicate
Firefox quicksearches
cmd.exe
tsunami
SQL2005: tables from strings
item name
tags
==Introduction "Duck typing"[1] describes programming where methods only care about certain attributes of their arguments. For example, a method might only care that an object has a `SaveToDb` method. Typically, this is supported in statically typed languages with interfaces, but interfaces can be clunky or impossible, in the case of wanting to treat existing, unmodifiable types as ducks. The code below provides a glimpse into how we can circumvent that in a relatively elegant fashion. The code below is fairly simple. For comprehensive, and _fast_ code, it is suggested that you check out something different, like the "project"[2] listed below. First read the below to get an idea of what this duck typing thing is about, then move onto the industrial-strength code for industrial use. ==Links - "Duck Typing Project"[2] - looks comprehensive, with lightweight code gen, but it is _complicated_ [1]http://en.wikipedia.org/wiki/Duck_typing [2]http://www.deftflux.net/blog/page/Duck-Typing-Project.aspx ==Code Note: [[C# 3.0 Anonymous Cheating, optimized/265]] gives a 30% speed boost to `CastPartial<T>`. {{ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using System.IO; namespace LinqCheating { class Program { static T CastExact<T>(object obj, T template) { return (T)obj; } static T CastPartial<T>(object obj, T template) { var propPairs = from p in typeof(T).GetProperties() join o in obj.GetType().GetProperties() on new { p.Name, p.PropertyType } equals new { o.Name, o.PropertyType } into g select new { Getter = g.SingleOrDefault(), Setter = p }; if (propPairs.Any(el => el.Getter == null)) throw new InvalidCastException(string.Format( "Attempted to cast {0} to {1}; the following properties were missing from {0}:\n" + propPairs .Where(el => el.Getter == null) .Select(el => string.Format(" {0} {1}", el.Setter.PropertyType.Name, el.Setter.Name)) .Join("\n"), obj.GetType().Name, typeof(T).Name)); return (T)typeof(T).GetConstructors().Single().Invoke( propPairs.Select(el => el.Getter.GetValue(obj, null)).ToArray()); } static IEnumerable<T> Pull<T>(object obj) { var props = from p in obj.GetType().GetProperties() where p.PropertyType == typeof(T) select p; return props.Select(p => (T)p.GetValue(obj, null)); } static IEnumerable<T> Pull<T>(object obj, T template) { var props = from p in obj.GetType().GetProperties() where p.PropertyType == typeof(T) select p; return props.Select(p => (T)p.GetValue(obj, null)); } static void CheatCastExact(object obj) { var cheated = CastExact(obj, new { A = default(string), B = default(string) }); Console.WriteLine(" " + cheated.B); } static void CheatCastPartial(object obj) { var cheated = CastPartial(obj, new { A = default(string) }); Console.WriteLine(" " + cheated.A); } static void CheatPull(object obj) { var cheated = Pull<string>(obj); foreach(var s in cheated) Console.WriteLine(" " + s); var other = Pull(obj, new { S = default(int), T = default(string) }); foreach (var v in other) Console.WriteLine(" " + v); } static void Time(string description, int iterations, Action a) { Stopwatch sw = Stopwatch.StartNew(); for (int i = 0; i < iterations; i++) a(); sw.Stop(); // so we can redirect stdout to null if we want Console.Error.WriteLine( "{0,7:0} ticks {1,7:0.000} ms ({2,6}) {3}", sw.Elapsed.Ticks, sw.Elapsed.TotalMilliseconds, iterations, description); } static void Execute<T>(string description, Action<T> a, T arg) { Console.WriteLine(description); Console.WriteLine("input: " + arg); Console.WriteLine("output:"); a(arg); Console.WriteLine(); } static void Main(string[] args) { var w = new { A = "a", B = "b", N = 1, Q = new { S = 1, T = "a" } }; Execute("CheatPull", CheatPull, w); Execute("CheatCastExact", CheatCastExact, new { A = "a", B = "b" }); Execute("CheatCastPartial", CheatCastPartial, w); Time("CheatCastPartial", 100000, () => CheatCastPartial(w)); Console.SetOut(TextWriter.Null); Time("CheatCastPartial", 100000, () => CheatCastPartial(w)); } } static class Functional { public static string Join(this IEnumerable<string> strings, string delimiter) { return strings.Any() ? string.Join(delimiter, strings as string[] ?? strings.ToArray()) : ""; } } } }} ==Output {{ CheatPull input: { A = a, B = b, N = 1, Q = { S = 1, T = a } } output: a b { S = 1, T = a } CheatCastExact input: { A = a, B = b } output: b CheatCastPartial input: { A = a, B = b, N = 1, Q = { S = 1, T = a } } output: a a a . . . a a 68588684 ticks 6858.868 ms (100000) CheatCastPartial 31065997 ticks 3106.600 ms (100000) CheatCastPartial }} ==Notes Anonymous types are declared in the _null_ namespace, which effectively makes them assembly-wide. Run this to verify for yourself: {{ Console.WriteLine(new { A = "wut" }.GetType().Namespace == null); }} From "namespace"[0]: "Whether or not you explicitly declare a namespace in a C# source file, the compiler adds a default namespace. This unnamed namespace, sometimes referred to as the global namespace, is present in every file. Any identifier in the global namespace is available for use in a named namespace." From "System.Type.Namespace"[1]: "A namespace is a logical design-time naming convenience, used mainly to define scope in an application and organize classes and other types in a single hierarchical structure. From the viewpoint of the runtime, there are no namespaces. ... From the viewpoint of the runtime, there are no namespaces." As any other type, anonymous types get unloaded when their containing "AppDomain"[2] is unloaded. Anonymous types are generated as `internal sealed`. [0]http://msdn.microsoft.com/en-us/library/z2kcy19k.aspx [1]http://msdn.microsoft.com/en-us/library/system.type.namespace.aspx [2]http://msdn.microsoft.com/en-us/library/system.appdomain.aspx
some permissive license goes here
contact