C# 3.x, LINQ and Associated Technologies

by Jared 8. February 2008 21:31

After lots of procrastination, I finally decided I was being left behind by the .Net community and started researching LINQ (Language INtegrated Query) and its underlying technologies - anonymous methods, anonymous types, lambda expressions (predicates and assignments), and extender methods etc. Ironically, I purchased an APress’ Pro LINQ exactly one day before finding out that Microsoft had made Introduction to Microsoft LINQ freely available (or nearly so - you need a Passport account, and have to aquiesce to a short personal info questionnaire). I don’t feel like my APress dollars were wasted, however. Their treatment of the different classes of LINQ (-to Objects, -to ADO.Net, -to XML, etc.) appears to be fairly exhaustive, where the ‘Intro’ volume looks like it lays the groundwork for the technology. One of the items that caught my eye immediately was the concept of extender methods, which allow developers to change the behavior of existing types - even (or perhaps particularly) sealed ones. Now, that’s an excellent idea.  For instance, if I wanted to fix things so that the System.String type implemented a ToPalindrome() method, I could do so using an extender method like so:

001public static class StringExtender
002{ 
003  public static string ToPalindrome(this string value)
004  {
005    if (string.IsNullOrEmpty(value) == false)
006      return value + value.Reverse();
007    return null;
008  }
009  public static string Reverse(this string value)
010  {
011    if (string.IsNullOrEmpty(value) == false)
012    {
013      char[] cArray = value.ToCharArray();
014      Array.Reverse(cArray);
015      return new string(cArray);
016    }
017    return null;
018  }
019}

…and yes, I know this is a very contrived example, but it still underscores a very cool new feature.  After looking at the extender methods concept for a little while, I started to wonder what would happen in the case of virtual methods where there was ambiguity between the virtual method and an extender with the same signature. As most .Net developers should (hopefully do) know, virtual members are (almost) always correctly resolved by the runtime, and their resolution certainly always adheres to specific rules. Most developers I know simply cite the "It is what it *IS*" rule, meaning that, no matter how an instance is declared, the type used to instantiate it is the one used to determine its behavior.Example: Assuming we have types A and B, and type B inherits from type A, and further assuming there is a virtual method on A called DoSomething that is overridden on B, then the following code would result in B's version of the method being executed every time...

A bInstance = new B();
bInstance.DoSomething(); // Executes B.DoSomething()

The only time we don't see this kind of behavior is when the method being replaced is not virtual, or the overrides keyword is not used. The resulting code will compile, but the behavior is different, and can be unexpected and confusing. The behavior is called 'shadowing' or 'hiding' and usually yields a compiler warning. However, when methods are 'hidden' in this way, the path to the appropriate virtual member cannot be determined at run-time, so the declared type makes a difference. What that means is that in the example above, if B.DoSomething() had hidden A.DoSomething(), the call to bInstance.DoSomething() would have executed A.DoSomething() instead.Confusing enough? The only reason I mention this is that the treatment of extender methods is handled in much the same way as shadowing or hiding, just in a more institutionalized manner. The rules are:

 

  1. Instance methods are always evaluated first.
  2. Extender methods are evaluated second.
  3. Virtual methods are evaluated last.

 

What the list above means, although it may not be obvious, is that the declared type (the "left side type name" of a variable declaration) of a variable matters where extender methods are concerned. Why? Because virtual members are resolved at run-time, but extender methods are linked at compile-time. Essentially, if there is an available extender method on the declared type, then any virtual version of the same method will be totally ignored at run-time - in fact, it won't even exist as a possibility in the byte code. The following example code, more or less a copy of a similar example in the Introduction to Microsoft LINQ volume, illustrates the effect, which I find very similar to the previous description of shadowing/hiding members:

001public class ParentType
002{
003    public virtual void X() { }
004}
005public class ChildType : ParentType
006{
007    public override void X() { }
008    public void Y() { }
009}
010public static class Extender
011{
012    static void X(this ParentType a) { }
013    static void Y(this ParentType b) { }
014    public static void TryItOut()
015    {
016        ParentType a = new ParentType();
017        ChildType b = new ChildType();
018        ParentType c = new ChildType();
019        a.X();  // Calls ParentType.X()
020        b.X();  // Calls ChildType.X()
021        c.X();  // Calls ChildType.X()
022        a.Y();  // Calls Extender.Y()
023        b.Y();  // Calls ChildType.Y()
024        c.Y();  // Calls Extender.Y()
025    }
026}

Now, that's about enough out of me for one day...

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags:
Categories: C#

Comments

Creative Commons License
This work is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License.