//
you're reading...
.NET, Extension Methods

How C# Extension Methods Work

1. Introduction.

1.1 Extension methods were introduced in C# 3.0.

1.2 The main purpose of extension methods is to provide for seamless integration of new functionalities to existing classes.

1.3 I have personally used extension methods to great effect in my projects and I intend to share some of my extension methods with readers in the future.

1.4 In this blog, I wish to explore how extension methods work under the hood.

1.5 We will explore low-level IL code generated for extension methods as well as their client code.

1.6 For a little bit of fun, I will also show how, with a little bit of IL code tweaking, we can turn a normal static method into an extension method.

2. A Sample Extension Method.

2.1 First let’s create a small C# class library with the following namespace and class :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StringExtensionsClassLib
{
    public static class StringHelperExtension
    {
        public static string MultipleAppend(this string myString, params string[] strParamList)
        {
            for (int i = 0; i < strParamList.Length; i++)
            {
                myString += strParamList[i];
            }

            return myString;
        }
    }
}
  • The above namespace StringHelperExtensionLib defines a class named StringHelperExtension.
  • The StringHelperExtension class is declared static and exposes one static method named MultipleAppend().
  • The MultipleAppend() method appends a string with a variable number of strings (via the C# params keyword).

2.2 Next, let’s write a simple console client that uses StringHelperExtension.MultipleAppend() :

using System;
using StringExtensionsClassLib;

namespace ConsoleTestExtensionMethods
{
    class Program
    {
        static void DoTest_ExtensionMethods()
        {
            Console.WriteLine("Hello".MultipleAppend(" I", " am", " string."));
            Console.ReadLine();
        }

        static void Main(string[] args)
        {
            DoTest_ExtensionMethods();
        }
    }
}

The console project will of course have to reference the StringExtensionsClassLib class library. The output of this program will be :

Hello I am string.

3. The IL Code For The StringExtensionsClassLib Class Library.

3.1 We can run ILDasm.exe to disassemble the StringExtensionsClassLib class library and examine its contents.

3.2 On my machine, in the output (\bin\Debug) folder of the StringExtensionsClassLib project, I run the following command :

ildasm StringExtensionsClassLib.dll /TEXT /OUT=StringExtensionsClassLib.il

3.3 A text file StringExtensionsClassLib.il will be output by ILDasm and the following is a scoped down listing :

//  Microsoft (R) .NET Framework IL Disassembler.  Version 4.6.81.0
//  Copyright (c) Microsoft Corporation.  All rights reserved.



// Metadata version: v4.0.30319
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 4:0:0:0
}
.assembly StringExtensionsClassLib
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) 
  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) 
      = ( 01 00 08 00 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() 
      = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78   // ....T..WrapNonEx
                                                                                                             63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 )       // ceptionThrows.

  ...
  ...
  ...

  .hash algorithm 0x00008004
  .ver 1:0:0:0
}
.module StringExtensionsClassLib.dll
// MVID: {D356267D-E21A-4BEC-94C6-D12D12BA8DDA}
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003       // WINDOWS_CUI
.corflags 0x00000003    //  ILONLY 32BITREQUIRED
// Image base: 0x00800000


// =============== CLASS MEMBERS DECLARATION ===================

.class public abstract auto ansi sealed beforefieldinit StringExtensionsClassLib.StringHelperExtension
       extends [mscorlib]System.Object
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) 
  .method public hidebysig static string 
          MultipleAppend(string myString,
                         string[] strParamList) cil managed
  {
    .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) 
    .param [2]
    .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) 
    // Code size       40 (0x28)
    .maxstack  3
    .locals init ([0] int32 i,
             [1] int32 V_1,
             [2] bool V_2,
             [3] string V_3)
    IL_0000:  nop
    IL_0001:  ldc.i4.0
    IL_0002:  stloc.0
    IL_0003:  br.s       IL_0018

    IL_0005:  nop
    IL_0006:  ldarg.0
    IL_0007:  ldarg.1
    IL_0008:  ldloc.0
    IL_0009:  ldelem.ref
    IL_000a:  call       string [mscorlib]System.String::Concat(string,
                                                                string)
    IL_000f:  starg.s    myString
    IL_0011:  nop
    IL_0012:  ldloc.0
    IL_0013:  stloc.1
    IL_0014:  ldloc.1
    IL_0015:  ldc.i4.1
    IL_0016:  add
    IL_0017:  stloc.0
    IL_0018:  ldloc.0
    IL_0019:  ldarg.1
    IL_001a:  ldlen
    IL_001b:  conv.i4
    IL_001c:  clt
    IL_001e:  stloc.2
    IL_001f:  ldloc.2
    IL_0020:  brtrue.s   IL_0005

    IL_0022:  ldarg.0
    IL_0023:  stloc.3
    IL_0024:  br.s       IL_0026

    IL_0026:  ldloc.3
    IL_0027:  ret
  } // end of method StringHelperExtension::MultipleAppend

} // end of class StringExtensionsClassLib.StringHelperExtension


// =============================================================

// *********** DISASSEMBLY COMPLETE ***********************
// WARNING: Created Win32 resource file StringExtensionsClassLib.res

The important parts of the code listing above are highlighted in bold. Each line is the same and is the following :

.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 )

This is essentially a declaration for the System.Runtime.CompilerServices.ExtensionAttribute. It is applied to 3 entities in the assembly :

  • The StringExtensionsClassLib assembly.
  • The StringHelperExtension class.
  • The MultipleAppend() method.

3.4 The ExtensionAttribute being applied to the assembly indicates that the assembly contains extension methods.

3.5 It being applied to the StringHelperExtension class is essentially as if we have declared the StringHelperExtension class as follows :

    [System.Runtime.CompilerServices.Extension]
    public static class StringHelperExtension
    {
        ...
        ...
        ...
    }

This  is important. It indicates that the class contains extension methods. Without this attribute being applied to the class, any extension methods in the class cannot be used by any client.

Note, however, that it is not correct to add the System.Runtime.CompilerServices.Extension attribute in code to the StringHelperExtension class. It will lead to a compilation error as follows :

Error CS1112 Do not use 'System.Runtime.CompilerServices.ExtensionAttribute'. Use the 'this' keyword instead.

The compilation error advises us to indicate that a class contains extension methods by using the “this” keyword on the first parameter of extension methods.

3.6 It being applied to the MultipleAppend() method is obvious. Likewise it is similar to adding the System.Runtime.CompilerServices.Extension attribute to the MultipleAppend() method :

        [System.Runtime.CompilerServices.Extension]
        public static string MultipleAppend(this string myString, params string[] strParamList)
        {
            for (int i = 0; i < strParamList.Length; i++)
            {
                myString += strParamList[i];
            }

            return myString;
        }

For reasons mentioned earlier, do not add the System.Runtime.CompilerServices.Extension attribute to the MultipleAppend() method. Doing so will cause the same compilation error that we saw earlier. The correct way to make a method an extension method is to use the “this” keyword on the first parameter declaration.

4. Manipulating The StringExtensionsClassLib  IL Code.

4.1 Let’s examine what it will be like if we were to remove the ExtensionAttribute from the definition of the MultipleAppend() in the IL code.

4.2 On my machine, I modified the StringExtensionsClassLib.il code generated by ILDasm.exe as follows :

.class public abstract auto ansi sealed beforefieldinit StringExtensionsClassLib.StringHelperExtension
       extends [mscorlib]System.Object
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) 
  .method public hidebysig static string 
          MultipleAppend(string myString,
                         string[] strParamList) cil managed
  {
    // Comment out the code below :
    //.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) 
    .param [2]
    .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) 
    // Code size       40 (0x28)
    .maxstack  3
    .locals init ([0] int32 i,
             [1] int32 V_1,
             [2] bool V_2,
             [3] string V_3)
    ...
    ...
    ...

The call to the constructor for the ExtensionAttribute of the MultipleAppend() method is commented out.

4.3 Next, run ILASM.exe to re-assemble the IL code back into a class library :

ilasm /DLL StringExtensionsClassLib.il /OUTPUT=StringExtensionsClassLib.dll

4.4 A new StringExtensionsClassLib.dll will be produced. This time, with the project of the test app ConsoleTestExtensionMethods.exe still referencing StringExtensionsClassLib.dll, we will not be able to compile. A compilation error will occur at the following line :

Console.WriteLine("Hello".MultipleAppend(" I", " am", " string."));

and the following compilation will be shown :

Error CS1061 'string' does not contain a definition for 'MultipleAppend' and no extension method 'MultipleAppend' 
accepting a first argument of type 'string' could be found

This error is complaining that the class for the string object with value “Hello” does not contain any definition for an extension method named MultipleAppend().

4.5 Let us examine the code for MultipleAppend() generated by the modified IL code. We do this by using a tool named ILSpy which is able to decompile the IL code for MultipleAppend(). Accoring to ILSpy, after reverse engineering the IL code, MultipleAppend() is defined as :

// StringExtensionsClassLib.StringHelperExtension
public static string MultipleAppend(string myString, params string[] strParamList)
{
    int num;
    for (int i = 0; i < strParamList.Length; i = num + 1)
    {
        myString += strParamList[i];
        num = i;
    }
    return myString;
}

Note that the “this” keyword originally appearing before the declaration of the “myString” parameter is removed. This effectively renders MultipleAppend() a non-extension method.

4.6 Let’s reverse the action we just did and uncomment the call to the constructor for the ExtensionAttribute of the MultipleAppend() method in the IL code. After uncommenting the line, we run ILasm.exe again.

4.7 Upon re-compiling the project of the test app ConsoleTestExtensionMethods.exe, it will be successful this time. When we look into the IL code again using ILSpy, we see the following :

// StringExtensionsClassLib.StringHelperExtension
public static string MultipleAppend(this string myString, params string[] strParamList)
{
    int num;
    for (int i = 0; i < strParamList.Length; i = num + 1)
    {
        myString += strParamList[i];
        num = i;
    }
    return myString;
}

The “this” keyword is re-instated. MultipleAppend() is once again an extension method.

5. How does the Client make the Call to the Extension Method ?

5.1 Let’s look under the hood inside the ConsoleTestExtensionMethods.exe client app and see how extension methods are actually called at low level. For this, we once again turn to ILDasm.exe :

  .method private hidebysig static void  DoTest_ExtensionMethods() cil managed
  {
    // Code size       54 (0x36)
    .maxstack  8
    IL_0000:  nop
    IL_0001:  ldstr      "Hello"
    IL_0006:  ldc.i4.3
    IL_0007:  newarr     [mscorlib]System.String
    IL_000c:  dup
    IL_000d:  ldc.i4.0
    IL_000e:  ldstr      " I"
    IL_0013:  stelem.ref
    IL_0014:  dup
    IL_0015:  ldc.i4.1
    IL_0016:  ldstr      " am"
    IL_001b:  stelem.ref
    IL_001c:  dup
    IL_001d:  ldc.i4.2
    IL_001e:  ldstr      " string."
    IL_0023:  stelem.ref
    IL_0024:  call       string [StringExtensionsClassLib]StringExtensionsClassLib.StringHelperExtension::MultipleAppend(string,
                                                                                                                         string[])
    IL_0029:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_002e:  nop
    IL_002f:  call       string [mscorlib]System.Console::ReadLine()
    IL_0034:  pop
    IL_0035:  ret
  } // end of method Program::DoTest_ExtensionMethods

From the IL code above we see that the call to MultipleAppend() is essentially a typical static method call. Hence the syntactic sugar of :

"Hello".MultipleAppend(" I", " am", " string.")

worked out to become a static method call with “Hello” being the first parameter :

IL_0001:  ldstr      "Hello"

5.2 Let me illustrate this in another way. Let’s say we have another class library named StringHelperClassLib with the following code :

using System;

namespace StringHelperClassLib
{
    public class StringHelper
    {
        public static string MultipleAppendHelper(string myString, params string[] strParamList)
        {
            for (int i = 0; i < strParamList.Length; i++)
            {
                myString += strParamList[i];
            }

            return myString;
        }
    }
}
  • We have a class named StringHelper with a static method named MultipleAppendHelper().
  • Note that MultipleAppendHelper() is not an extension method.
  • But, with the exception of the missing “this” declaration for the “myString” parameter, StringHelper.MultipleAppendHelper() and the earlier StringHelperExtension.MultipleAppend() are exactly the same.

5.3 Let’s say we enhance the code for the test app ConsoleTestExtensionMethods.exe as follows :

using System;
using StringExtensionsClassLib;
using StringHelperClassLib;

namespace ConsoleTestExtensionMethods
{
    class Program
    {
        static void DoTest_ExtensionMethods()
        {
            Console.WriteLine("Hello".MultipleAppend(" I", " am", " string."));
            Console.ReadLine();
        }

        static void DoTest_StaticMethods()
        {
            Console.WriteLine(StringHelper.MultipleAppendHelper("Hello", " I", " am", " string."));
            Console.ReadLine();
        }

        static void Main(string[] args)
        {
            DoTest_ExtensionMethods();
            DoTest_StaticMethods();
        }
    }
}
  • Note our reference to the StringHelperClassLib namespace and the new DoTest_StaticMethods() method.
  • The DoTest_StaticMethods() method makes a static call to MultipleAppendHelper() with “Hello” as the first parameter.

5.4 Running ILDasm.exe once again, we will obtain a definition for DoTest_StaticMethods(). Let’s compare them both :

  .method private hidebysig static void  DoTest_ExtensionMethods() cil managed
  {
    // Code size       54 (0x36)
    .maxstack  8
    IL_0000:  nop
    IL_0001:  ldstr      "Hello"
    IL_0006:  ldc.i4.3
    IL_0007:  newarr     [mscorlib]System.String
    IL_000c:  dup
    IL_000d:  ldc.i4.0
    IL_000e:  ldstr      " I"
    IL_0013:  stelem.ref
    IL_0014:  dup
    IL_0015:  ldc.i4.1
    IL_0016:  ldstr      " am"
    IL_001b:  stelem.ref
    IL_001c:  dup
    IL_001d:  ldc.i4.2
    IL_001e:  ldstr      " string."
    IL_0023:  stelem.ref
    IL_0024:  call       string [StringExtensionsClassLib]StringExtensionsClassLib.StringHelperExtension::MultipleAppend(string,
                                                                                                                         string[])
    IL_0029:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_002e:  nop
    IL_002f:  call       string [mscorlib]System.Console::ReadLine()
    IL_0034:  pop
    IL_0035:  ret
  } // end of method Program::DoTest_ExtensionMethods

  .method private hidebysig static void  DoTest_StaticMethods() cil managed
  {
    // Code size       54 (0x36)
    .maxstack  8
    IL_0000:  nop
    IL_0001:  ldstr      "Hello"
    IL_0006:  ldc.i4.3
    IL_0007:  newarr     [mscorlib]System.String
    IL_000c:  dup
    IL_000d:  ldc.i4.0
    IL_000e:  ldstr      " I"
    IL_0013:  stelem.ref
    IL_0014:  dup
    IL_0015:  ldc.i4.1
    IL_0016:  ldstr      " am"
    IL_001b:  stelem.ref
    IL_001c:  dup
    IL_001d:  ldc.i4.2
    IL_001e:  ldstr      " string."
    IL_0023:  stelem.ref
    IL_0024:  call       string [StringHelperClassLib]StringHelperClassLib.StringHelper::MultipleAppendHelper(string,
                                                                                                              string[])
    IL_0029:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_002e:  nop
    IL_002f:  call       string [mscorlib]System.Console::ReadLine()
    IL_0034:  pop
    IL_0035:  ret
  } // end of method Program::DoTest_StaticMethods

You will note that with the exception of line IL_0024 in each of the methods :

    IL_0024:  call       string [StringExtensionsClassLib]StringExtensionsClassLib.StringHelperExtension::MultipleAppend(string,
                                                                                                                         string[])

    ...
    ...
    ...

    IL_0024:  call       string [StringHelperClassLib]StringHelperClassLib.StringHelper::MultipleAppendHelper(string,
                                                                                                              string[])

The other parts of the IL codes for the two methods are the same.

5.5 This demonstrates that an extension method call essentially works out to be a call to a static method.

5.6 The syntactic sugar that we have been allowed to use :

"Hello".MultipleAppend(" I", " am", " string.")

gets transformed at compile time into a call to a static method. It is as if something like the following code was used instead :

StringHelperExtension.MultipleAppend("Hello", " I", " am", " string.")

6. Turning A Non-Extension Method Into One.

6.1 What would happen if we tried to surreptitiously insert the line :

.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 )

into the StringHelperClassLib class library ? Specifically into the definition for the MultipleAppendHelper() method ? Would it turn MultipleAppendHelper() into an extension method ?

6.2 Let’s give it a try. Once again, we use ILDasm.exe to disassemble the StringHelperClassLib class library into IL code :

ildasm StringHelperClassLib.dll /TEXT /OUT=StringHelperClassLib.il

The StringHelperClassLib.il file will be produced.

6.3 We then augment the IL code as follows :

//  Microsoft (R) .NET Framework IL Disassembler.  Version 4.6.81.0
//  Copyright (c) Microsoft Corporation.  All rights reserved.



// Metadata version: v4.0.30319
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 4:0:0:0
}
.assembly StringHelperClassLib
{
    ...
    ...
    ...
}
.module StringHelperClassLib.dll
// MVID: {A919FEA3-E35F-438F-9123-2D009F5D33D7}
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003       // WINDOWS_CUI
.corflags 0x00000003    //  ILONLY 32BITREQUIRED
// Image base: 0x00C20000


// =============== CLASS MEMBERS DECLARATION ===================

.class public auto ansi beforefieldinit StringHelperClassLib.StringHelper
       extends [mscorlib]System.Object
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) 
  .method public hidebysig static string 
          MultipleAppendHelper(string myString,
                               string[] strParamList) cil managed
  {
    .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) 
    .param [2]
    .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) 
    // Code size       40 (0x28)
    .maxstack  3
    .locals init ([0] int32 i,
             [1] int32 V_1,
             [2] bool V_2,
             [3] string V_3)
    IL_0000:  nop
    IL_0001:  ldc.i4.0
    IL_0002:  stloc.0
    IL_0003:  br.s       IL_0018

    IL_0005:  nop
    IL_0006:  ldarg.0
    IL_0007:  ldarg.1
    IL_0008:  ldloc.0
    IL_0009:  ldelem.ref
    IL_000a:  call       string [mscorlib]System.String::Concat(string,
                                                                string)
    IL_000f:  starg.s    myString
    IL_0011:  nop
    IL_0012:  ldloc.0
    IL_0013:  stloc.1
    IL_0014:  ldloc.1
    IL_0015:  ldc.i4.1
    IL_0016:  add
    IL_0017:  stloc.0
    IL_0018:  ldloc.0
    IL_0019:  ldarg.1
    IL_001a:  ldlen
    IL_001b:  conv.i4
    IL_001c:  clt
    IL_001e:  stloc.2
    IL_001f:  ldloc.2
    IL_0020:  brtrue.s   IL_0005

    IL_0022:  ldarg.0
    IL_0023:  stloc.3
    IL_0024:  br.s       IL_0026

    IL_0026:  ldloc.3
    IL_0027:  ret
  } // end of method StringHelper::MultipleAppendHelper

  ...
  ...
  ...
} // end of class StringHelperClassLib.StringHelper


// =============================================================

// *********** DISASSEMBLY COMPLETE ***********************
// WARNING: Created Win32 resource file StringHelperClassLib.res

We added the lines as indicated above in bold.

6.4 We then run ILasm.exe on this IL code :

ilasm /DLL StringHelperClassLib.il /OUTPUT=StringHelperClassLib.dll

A new StringHelperClassLib.dll class library will be produced.

6.5 We then modify the source codes of the ConsoleTestExtensionMethods.exe test application once more :

using System;
using StringExtensionsClassLib;
using StringHelperClassLib;

namespace ConsoleTestExtensionMethods
{
    class Program
    {
        static void DoTest_ExtensionMethods()
        {
            Console.WriteLine("Hello".MultipleAppend(" I", " am", " string."));
            Console.ReadLine();
        }

        static void DoTest_StaticMethods()
        {
            Console.WriteLine(StringHelper.MultipleAppendHelper("Hello", " I", " am", " string."));
            Console.ReadLine();
        }

        static void DoTest_StaticToExtensionMethods()
        {
            Console.WriteLine("Hello".MultipleAppendHelper(" I", " am", " string."));
            Console.ReadLine();
        }

        static void Main(string[] args)
        {
            DoTest_ExtensionMethods();
            DoTest_StaticMethods();
            DoTest_StaticToExtensionMethods();
        }
    }
}
  • We added a new method named DoTest_StaticToExtensionMethods() and called it inside Main().
  • The source codes will compile successfully and when run, the test app will produce the following expected output :
Hello I am string.

Hello I am string.

Hello I am string.

6.6 This demonstrates how we can turn a static method of a normal class into an extension method. Notice that the StringHelper class is not even a static class.

6.7 However, note that this demonstration is only a gimmick designed to help the reader understand the significance of the System.Runtime.CompilerServices.ExtensionAttribute. This is not the way to define extension methods. Do not do this other than for experimentation purposes.

7. In Conclusion.

7.1 Extension methods are a great way to provide syntactic sugar in your code.

7.2 Syntactic sugars, when applied well, can make code much more understandable.

7.3 If one thinks it through, one would realize that all high-level programming languages are an evolvement of syntactic sugar.

7.4 In these days of fast processors and abundant memory, terse and minimalist coding are things of the past.

7.5 I personally consider code readability the future of software engineering. Extension methods, LINQ and operator overloading are prime examples.

Advertisements

About Lim Bio Liong

I've been in software development for nearly 20 years specializing in C , COM and C#. It's truly an exicting time we live in, with so much resources at our disposal to gain and share knowledge. I hope my blog will serve a small part in this global knowledge sharing network. For many years now I've been deeply involved with C development work. However since circa 2010, my current work has required me to use more and more on C# with a particular focus on COM interop. I've also written several articles for CodeProject. However, in recent years I've concentrated my time more on helping others in the MSDN forums. Please feel free to leave a comment whenever you have any constructive criticism over any of my blog posts.

Discussion

No comments yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: