Really funny read. Check it out here.
Wednesday, August 23, 2006
Friday, August 18, 2006
How to do late Dynamic method creation
In this post comment, I was asked about how to do creation of method delegates whose types can vary somewhat when using the Dynamic
class. It's really a matter of using the unspecified form of a generic class and the Type.MakeGenericType
method to synthesize up the desired fully specified type. This code is going to look ugly, simply because playing with generics this way is more akin to writing a compiler than writing normal code. Much of this code is boiler-plate and could easily be extracted into a set of helper methods... given some time, I'll do so in my current Dynamic
class. Until then, here's an example of what things can look like:
// needed by both versions BindingFlags creatorFlags = BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Static; Type staticProc = typeof(Dynamic<>.Static.Procedure.Explicit<,,>); // this version "knows" the final argument type of the method, and could have been done explicitly // I show this to make obvious the way you use MakeGenericType. Type doubleProc = staticProc.MakeGenericType(typeof(PutMethods), typeof(IIdentifiable), typeof(string), typeof(double)); MethodInfo doubleProcCreateDelegate = doubleProc.GetMethod("CreateDelegate", creatorFlags, null, new Type[] { typeof(string) }, null); StaticProc<PutMethods, IIdentifiable, string, double> proc = doubleProcCreateDelegate.Invoke(null, new object[] { "Add" }) as StaticProc<PutMethods, IIdentifiable, string, double> proc(null, "Age", 1.0); // this is more like a normal use, where the final argument type is not known at delegate generation time... Type lazyProc = staticProc.MakeGenericType(typeof(PutMethods), typeof(IIdentifiable), typeof(string), typeof(object)); MethodInfo lazyProcCreateDelegate = lazyProc.GetMethod("CreateDelegate", creatorFlags, null, new Type[] { typeof(string) }, null); StaticProc<PutMethods, IIdentifiable, string, object> lazy = lazyProcCreateDelegate.Invoke(null, new object[] { "Add" }) as StaticProc<PutMethods, IIdentifiable, string, object> lazy(null, "Balance", -10.0); public interface IIdentifiable { void SetProperty(string propertyName, object value); } public class PutMethods { public static void Add(IIdentifiable dr, string propertyName, double value) { if (dr != null) dr.SetProperty(propertyName, value); } }
Note that you can easily build up the Type[]
passed to Type.MakeGenericType
based on things like an attribute class or configuration/XML markup.
Tuesday, August 15, 2006
The step-child gets some love...
Looks like Google is finally going to update Blog*Spot (a.k.a. Blogger in hosted mode). Coming soon:
- Dynamic content (not static pages) so no more republishing.
- Access Control so you control who can read your blog.
- Labels to let you tag the posts (why they couldn't call them tags, like the rest of the world, I don't know).
- Easy layout to let people that don't get CSS or HTML markup do drag-and-drop stying of the blog.
- More feed formats so you can choose the format you want to publish, and the default of Atom 0.3 will change to Atom 1.0.
I'm waiting anxiously to migrate this blog there and start tagging labelling things.
More information here.
Monday, August 14, 2006
Trying out Windows Live Writer...
I've used "Send to Blogger...", I've used "w:Blogger", so why not try Microsoft's latest stab at Googledom? Looks neat so far. You can get more information over at Introducing Windows Live Writer
Tuesday, August 08, 2006
They're back!
Putting it all together...
Jeremy D. Miller recapitulates the Best of the Shade Tree Developer. I nominate this as the most useful post of the year.
Wednesday, August 02, 2006
Yield to the power of the DataSource
The new declarative model for data sources in ASP.Net 2.0 is very seductive in its simplicity. I've all-too-often seen people doing date-picker and time-pickers by listing the times individually in ListItem
s. This leads to pages that have to be manually revisited if you change the range (start-time and end-time) or the increment (e.g. on-the-hour, on-the-half, etc.). It would be really cool to be able to drive these lists declaratively and bind them using the standard DataSourceID
logic.
Using the new C# yield
keyword it is trivial to return an IEnumerable<DateTime>
that lists all the times (with a specified increment in minutes), days, weeks, months, etc. in a date range. I give you DateTimeDataSource:
using System; using System.Collections.Generic; using System.ComponentModel; [DataObject(true)] public class DateTimeDataSource { private TimeSpan _DayStart = TimeSpan.FromHours(8); // default to 8am private TimeSpan _DayEnd = TimeSpan.FromHours(18); // default to 6pm [Browsable(true)] public TimeSpan DayStart { get { return _DayStart; } set { _DayStart = value; } } [Browsable(true)] public TimeSpan DayEnd { get { return _DayEnd; } set { _DayEnd = value; } } public static DateTime WeekStart(DateTime date) { return date.Date.AddDays(-(int)date.DayOfWeek); } public static DateTime WeekEnd(DateTime date) { return date.Date.AddDays(7).AddTicks(-1); } public static DateTime MonthStart(DateTime date) { return date.Date.AddDays(1 - date.Day); } public static DateTime MonthEnd(DateTime date) { return date.Date.AddMonths(1).AddTicks(-1); } public static DateTime YearStart(DateTime date) { return new DateTime(date.Year, 1, 1); } public static DateTime YearEnd(DateTime date) { return new DateTime(date.Year + 1, 1, 1).AddTicks(-1); } [DataObjectMethod(DataObjectMethodType.Select, true)] public IEnumerable<DateTime> ThisWeek() { DateTime start = WeekStart(DateTime.Today); DateTime end = WeekEnd(start); return Any(start, end, TimeSpan.FromDays(1)); } [DataObjectMethod(DataObjectMethodType.Select, true)] public IEnumerable<DateTime> ThisMonth() { DateTime start = MonthStart(DateTime.Today); DateTime end = MonthEnd(start); return Any(start, end, TimeSpan.FromDays(1)); } [DataObjectMethod(DataObjectMethodType.Select, true)] public IEnumerable<DateTime> Today(int minuteIncrement) { return Any(DateTime.MinValue, DateTime.MaxValue, minuteIncrement); } [DataObjectMethod(DataObjectMethodType.Select, false)] public IEnumerable<DateTime> Today(TimeSpan increment) { return Any(DateTime.MinValue, DateTime.MaxValue, increment); } [DataObjectMethod(DataObjectMethodType.Select, true)] public IEnumerable<DateTime> Weeks(int numberOfWeeks) { DateTime start = WeekStart(DateTime.Today); DateTime end = start + TimeSpan.FromDays(7 * numberOfWeeks); return Weeks(start, end); } [DataObjectMethod(DataObjectMethodType.Select, false)] public IEnumerable<DateTime> Weeks(DateTime start, int numberOfWeeks) { DateTime end = start + TimeSpan.FromDays(7 * numberOfWeeks); return Weeks(start, end); } [DataObjectMethod(DataObjectMethodType.Select, false)] public IEnumerable<DateTime> Weeks(DateTime start, DateTime end) { return Any(start, end, TimeSpan.FromDays(7)); } [DataObjectMethod(DataObjectMethodType.Select, true)] public IEnumerable<DateTime> Any(DateTime start, DateTime end, int minuteIncrement) { return Any(start, end, TimeSpan.FromMinutes(minuteIncrement)); } [DataObjectMethod(DataObjectMethodType.Select, false)] public IEnumerable<DateTime> Any(DateTime start, DateTime end, TimeSpan increment) { if (start == DateTime.MinValue || start.Ticks == 0) { start = DateTime.Today.Date + DayStart; } if (end == DateTime.MaxValue || end.Ticks == 0) { end = DateTime.Today.Date + DayEnd; } for (DateTime current = start; current <= end; current += increment) { yield return current; } } }
Use this just like you would any other data source, for example, to build a RadioButtonList
for the normal working hours (8am to 6pm, see notes below), you can do this:
<asp:ObjectDataSource ID="Hours" runat="server" CacheDuration="Infinite" EnableCaching="true" TypeName="DateTimeDataSource" SelectMethod="Today"> <SelectParameters> <asp:Parameter Name="minuteIncrement" Type="Int32" Direction="Input" DefaultValue="30" /> </SelectParameters> </asp:ObjectDataSource> <asp:RadioButtonList ID="RadioButtonTimeFrom" runat="server" DataSourceID="Hours" DataTextFormatString="{0:t}" />
Notes:
- The properties for
DayStart
andDayEnd
let you change the default start and end time for time-pickers. - The XXXXStart and XXXXEnd static methods are there to simplify the generation of date ranges.
- This isn't necessarily completely correct with other calendar forms.
UPDATE: Blogger ate my || in the code of Any