In F# you can write an exten­sion method like this:

image

Whilst this will work per­fectly fine in your F# code, the exten­sion method will not be vis­i­ble to any C# code using the File­Info type because F# and C# com­piles exten­sion meth­ods differently.

To make C#-compatible exten­sion meth­ods in F#, here’s what you need to do instead:

image

That’s it, just fol­low these 3 sim­ple steps and you’re done:

  1. wrap the exten­sion meth­ods inside a class dec­o­rated with the [<Exten­sion>] attribute
  2. write the exten­sion meth­ods as sta­tic mem­bers where the first argu­ment is of the type which should be extended (like how you would write an exten­sion method in C#)
  3. mark the exten­sion meth­ods with the [<Exten­sion>] attribute
Share

Cranked these out last night, hope you find it use­ful too Smile

   1: public static class SerializationExtensions

   2: {

   3:     private static readonly ConcurrentDictionary<Type, XmlSerializer> XmlSerializers =

   4:         new ConcurrentDictionary<Type, XmlSerializer>();

   5:  

   6:     private static readonly ConcurrentDictionary<Type, DataContractJsonSerializer> JsonSerializers =

   7:         new ConcurrentDictionary<Type, DataContractJsonSerializer>();

   8:  

   9:     private static readonly JavaScriptSerializer JavaScriptSerializer = new JavaScriptSerializer();

  10:  

  11:     public enum JsonSerializerType

  12:     {

  13:         Undefined = 0,

  14:  

  15:         DataContractJsonSerializer = 1,

  16:  

  17:         JavaScriptSerializer = 2

  18:     }

  19:  

  20:     /// <summary>

  21:     /// Serializes the object using XML format.

  22:     /// </summary>

  23:     public static string SerializeAsXml(this object obj)

  24:     {

  25:         var type = obj.GetType();

  26:         var xmlSerializer = GetXmlSerializer(type);

  27:  

  28:         using (var memStream = new MemoryStream())

  29:         {

  30:             xmlSerializer.Serialize(memStream, obj);

  31:             return Encoding.Default.GetString(memStream.ToArray());

  32:         }

  33:     }

  34:  

  35:     /// <summary>

  36:     /// Serializes the object using JSON format, either using the default 

  37:     /// DataContractJsonSerializer or the JavaScriptSerializer, the results

  38:     /// differ slightly.

  39:     /// </summary>

  40:     public static string SerializeAsJson(

  41:         this object obj, 

  42:         JsonSerializerType serializerType = JsonSerializerType.DataContractJsonSerializer)

  43:     {

  44:         switch (serializerType)

  45:         {

  46:             case JsonSerializerType.JavaScriptSerializer:

  47:                 return JavaScriptSerializer.Serialize(obj);

  48:             case JsonSerializerType.DataContractJsonSerializer:

  49:             default:

  50:                 var type = obj.GetType();

  51:                 var serializer = GetJsonSerializer(type);

  52:  

  53:                 using (var memStream = new MemoryStream())

  54:                 {

  55:                     serializer.WriteObject(memStream, obj);

  56:                     return Encoding.Default.GetString(memStream.ToArray());

  57:                 }

  58:         }

  59:     }

  60:     

  61:     /// <summary>

  62:     /// Deserializes the specified XML string to an object of the specified type T.

  63:     /// </summary>

  64:     public static T DeserializeAsXml<T>(this string xmlString)

  65:     {

  66:         var xmlSerializer = GetXmlSerializer(typeof(T));

  67:         using (var memStream = new MemoryStream(Encoding.Default.GetBytes(xmlString)))

  68:         {

  69:             return (T)xmlSerializer.Deserialize(memStream);

  70:         }

  71:     }

  72:  

  73:     /// <summary>

  74:     /// Deserializes the specified JSON string to an object of the specified type T.

  75:     /// </summary>

  76:     public static T DeserializeAsJson<T>(

  77:         this string jsonString, 

  78:         JsonSerializerType serializerType = JsonSerializerType.DataContractJsonSerializer)

  79:     {

  80:         switch (serializerType)

  81:         {

  82:             case JsonSerializerType.JavaScriptSerializer:

  83:                 return JavaScriptSerializer.Deserialize<T>(jsonString);

  84:             case JsonSerializerType.DataContractJsonSerializer:

  85:             default:

  86:                 var serializer = GetJsonSerializer(typeof(T));

  87:  

  88:                 using (var memStream = new MemoryStream(Encoding.Default.GetBytes(jsonString)))

  89:                 {

  90:                     return (T)serializer.ReadObject(memStream);

  91:                 }

  92:         }

  93:     }

  94:  

  95:     private static XmlSerializer GetXmlSerializer(Type type)

  96:     {

  97:         // gets the xml serializer from the concurrent dictionary, if it doesn't exist

  98:         // then add one for the specified type

  99:         return XmlSerializers.GetOrAdd(type, t => new XmlSerializer(t));

 100:     }

 101:  

 102:     private static DataContractJsonSerializer GetJsonSerializer(Type type)

 103:     {

 104:         // gets the json serializer from the concurrent dictionary, if it doesn't exist

 105:         // then add one for the specified type

 106:         return JsonSerializers.GetOrAdd(type, t => new DataContractJsonSerializer(t));

 107:     }

 108: }

Share
   1: /// <summary>

   2: /// Parses a string as a short

   3: /// </summary>

   4: public static short ParseShort(this string str, bool throwOnOverflow = true)

   5: {

   6:     return str.ParseAsStruct(Convert.ToInt16, throwOnOverflow);

   7: }

   8:  

   9: /// <summary>

  10: /// Parses a string as an ushort

  11: /// </summary>

  12: public static ushort ParseUshort(this string str, bool throwOnOverflow = true)

  13: {

  14:     return str.ParseAsStruct(Convert.ToUInt16, throwOnOverflow);

  15: }

  16:  

  17: /// <summary>

  18: /// Parses a string as an int

  19: /// </summary>

  20: public static int ParseInt(this string str, bool throwOnOverflow = true)

  21: {

  22:     return str.ParseAsStruct(Convert.ToInt32, throwOnOverflow);

  23: }

  24:  

  25: /// <summary>

  26: /// Parses a string as an uint

  27: /// </summary>

  28: public static uint ParseUint(this string str, bool throwOnOverflow = true)

  29: {

  30:     return str.ParseAsStruct(Convert.ToUInt32, throwOnOverflow);

  31: }

  32:  

  33: /// <summary>

  34: /// Parses a string as a long

  35: /// </summary>

  36: public static long ParseLong(this string str, bool throwOnOverflow = true)

  37: {

  38:     return str.ParseAsStruct(Convert.ToInt64, throwOnOverflow);

  39: }

  40:  

  41: /// <summary>

  42: /// Parses a string as an ulong

  43: /// </summary>

  44: public static ulong ParseUlong(this string str, bool throwOnOverflow = true)

  45: {

  46:     return str.ParseAsStruct(Convert.ToUInt64, throwOnOverflow);

  47: }

  48:  

  49: /// <summary>

  50: /// Parses a string as a float

  51: /// </summary>

  52: public static float ParseFloat(this string str, bool throwOnOverflow = true)

  53: {

  54:     return str.ParseAsStruct(Convert.ToSingle, throwOnOverflow);

  55: }

  56:  

  57: /// <summary>

  58: /// Parses a string as a double

  59: /// </summary>

  60: public static double ParseDouble(this string str, bool throwOnOverflow = true)

  61: {

  62:     return str.ParseAsStruct(Convert.ToDouble, throwOnOverflow);

  63: }

  64:  

  65: /// <summary>

  66: /// Parses a string as a date time value

  67: /// </summary>

  68: public static DateTime ParseDateTime(this string str)

  69: {

  70:     return str.ParseAsStruct(DateTime.Parse);

  71: }

  72:  

  73: /// <summary>

  74: /// Parses a string as a date time value using the exact format string

  75: /// </summary>

  76: public static DateTime ParseDateTimeExact(this string str, string format)

  77: {            

  78:     return str.ParseAsStruct(s => DateTime.ParseExact(s, format, CultureInfo.InvariantCulture));

  79: }

  80:  

  81: /// <summary>

  82: /// Parses a string as a boolean value

  83: /// </summary>

  84: public static bool ParseBoolean(this string str)

  85: {

  86:     return str.ParseAsStruct(Convert.ToBoolean);

  87: }

  88:  

  89: /// <summary>

  90: /// Parses a string as an enum value

  91: /// </summary>

  92: public static TEnum ParseEnum<TEnum>(this string str, bool ignoreCase = true) where TEnum : struct

  93: {

  94:     TEnum result;

  95:     return Enum.TryParse(str, ignoreCase, out result) ? result : default(TEnum);

  96: }        

  97:  

  98: private static T ParseAsStruct<T>(

  99:     this string str, Func<string, T> convert, bool throwOnOverflow = true) 

 100:     where T : struct

 101: {

 102:     if (throwOnOverflow)

 103:     {

 104:         checked

 105:         {

 106:             return convert(str);

 107:         }

 108:     }

 109:  

 110:     unchecked

 111:     {

 112:         return convert(str);

 113:     }

 114: }

Share

As you know, the ExpandoOb­ject class imple­ments the IDictionary<string, object> inter­face, so if you have an ExpandoOb­ject you can eas­ily cast it to an IDictionary<string, object> but there’s no built-in way to eas­ily do the reverse.

Luck­ily, I came across a very use­ful exten­sion method today which con­verts an IDictionary<string, object> into an ExpandoOb­ject, which you can then use dynam­i­cally in your code, sweet! :-)

With some small mod­i­fi­ca­tions, here’s the code I ended up with, with some com­ments thrown in for good measures:

   1: /// <summary>

   2: /// Extension method that turns a dictionary of string and object to an ExpandoObject

   3: /// </summary>

   4: public static ExpandoObject ToExpando(this IDictionary<string, object> dictionary)

   5: {

   6:     var expando = new ExpandoObject();

   7:     var expandoDic = (IDictionary<string, object>)expando;

   8:  

   9:     // go through the items in the dictionary and copy over the key value pairs)

  10:     foreach (var kvp in dictionary)

  11:     {

  12:         // if the value can also be turned into an ExpandoObject, then do it!

  13:         if (kvp.Value is IDictionary<string, object>)

  14:         {

  15:             var expandoValue = ((IDictionary<string, object>)kvp.Value).ToExpando();

  16:             expandoDic.Add(kvp.Key, expandoValue);

  17:         }

  18:         else if (kvp.Value is ICollection)

  19:         {

  20:             // iterate through the collection and convert any strin-object dictionaries

  21:             // along the way into expando objects

  22:             var itemList = new List<object>();

  23:             foreach (var item in (ICollection)kvp.Value)

  24:             {

  25:                 if (item is IDictionary<string, object>)

  26:                 {

  27:                     var expandoItem = ((IDictionary<string, object>) item).ToExpando();

  28:                     itemList.Add(expandoItem);

  29:                 }

  30:                 else

  31:                 {

  32:                     itemList.Add(item);

  33:                 }

  34:             }

  35:  

  36:             expandoDic.Add(kvp.Key, itemList);

  37:         }

  38:         else

  39:         {

  40:             expandoDic.Add(kvp);

  41:         }

  42:     }

  43:  

  44:     return expando;

  45: }

Enjoy!

Share

Ever tried to use IEnumerable<T>.Sum on an array of unsigned long inte­gers? Well, you can’t, because the Sum method has not been imple­mented for ulong or ulong?, so to fill in the gap here’s the exten­sion meth­ods you need using more or less the same code as the exist­ing Sum methods:

[CheckParameters]
public static ulong Sum([NotNull] this IEnumerable<ulong> source)
{
    var sum = 0UL;
    foreach (var number in source)
    {
        sum += number;
    }
    return sum;
}

[CheckParameters]
public static ulong? Sum([NotNull] this IEnumerable<ulong?> source)
{
    var sum = 0UL;
    foreach (var nullable in source)
    {
        if (nullable.HasValue)
        {
            sum += nullable.GetValueOrDefault();
        }
    }
    return sum;
}

[CheckParameters]
public static ulong Sum<T>([NotNull] this IEnumerable<T> source, Func<T, ulong> selector)
{
    return source.Select(selector).Sum();
}

[CheckParameters]
public static ulong? Sum<T>([NotNull] this IEnumerable<T> source, Func<T, ulong?> selector)
{
    return source.Select(selector).Sum();
}

I used some cus­tom Post­Sharp attrib­utes here to do the para­me­ter val­i­da­tion, but you can just as eas­ily sub­sti­tute them with if null then throw excep­tion code blocks.

UPDATE 10/11/2010:

Using the dynamic type in .Net 4 you can make these exten­sion meth­ods even more use­ful by mak­ing them usable with other value types too.

Tra­di­tion­ally for exten­sion meth­ods like Sum, you’d have to pro­vide an over­load for each numeric value type (int, uint, long, etc.) because these numeric value types don’t have a com­mon super type which defines the numeric oper­a­tors +, -, /, *, etc.

For­tu­nately, you can now negate this com­pile time lim­i­ta­tion by mak­ing it a run­time deci­sion using the new dynamic capabilities:

public static T Sum<T>(this IEnumerable<T> source) where T : struct
{
    return source.Aggregate(default(T), (current, number) => (dynamic) current + number);
}
public static T? Sum<T>(this IEnumerable<T?> source) where T : struct
{
    return source.Where(nullable => nullable.HasValue)
                 .Aggregate(
                     default(T),
                     (current, nullable) => (dynamic) current + nullable.GetValueOrDefault());
}
public static V Sum<T, V>(this IEnumerable<T> source, Func<T, V> selector) where V : struct
{
    return source.Select(selector).Sum();
}
public static V? Sum<T, V>(this IEnumerable<T> source, Func<T, V?> selector) where V : struct
{
    return source.Select(selector).Sum();
}

The obvi­ous fal­lacy with this approach is that you can now pass cus­tom struc­tures with no defined + oper­a­tor into these exten­sion meth­ods and no com­pile errors will be thrown, but a Run­time­BinderEx­cep­tion will be thrown by the DLR at run­time with a mes­sage like this:

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Oper­a­tor ‘+’ can­not be applied to operands of type ‘xxx’ and ‘xxx’

Share