Friday, July 09, 2004

Joc talks about a really cool feature of Whidbey that I hadn’t discovered yet: Tracepoints.

Tracepoints are set like breakpoints – you can toggle them on and off in the IDE, and you get little red stop signs in code view. However, instead of stopping execution when they get hit, you can configure them to output a message to the debug stream.

That strikes as being really useful. Debug-by-printf can be a useful technique; the ability to add and remove trace messages without recompiling code seems like it would not only keep code clean (by removing superfluous trace statements), but be really useful for the targeted debugging of hard-to-reproduce problems.

Check out Joc's post for more information.

Thursday, July 08, 2004 11:27:07 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0]
 Tuesday, July 06, 2004

To answer a question I posed earlier, about how KeyedCollection is different than Dictionary: a quick check of the documentation reveals that KeyedCollection is obsolete.

Well, that settles that.

 

Tuesday, July 06, 2004 7:03:28 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0]
 Sunday, November 23, 2003

Getting a chance to catch up on Mike Deem’s excellent WinFS blog. I’m learning quite a bit.

It’s good to see that WinFS will share the same query language as ObjectSpaces – namely, OPath. OPath is a SQL-like query language for executing queries against object trees that that represent SQL-backed data. Under the hood, I think OPath gets converted into an equivalent SQL query which is executed against the data store, with the results being stuffed back into a nice strongly-typed object tree. This seems to be how ObjectSpaces work, and I’m guessing that WinFS works in much the same way.

Anyway, it’s interesting to see that OPath queries are nothing more than strings. From the looks of examples like this, these strings can get rather complex (code borrowed from Mike Deem’s blog):

Person.FindAll(“IncomingRelationships.Cast(System.Storage.Contacts.HouseholdMember).Household.OutgoingRelationships.Cast(System.Storage.Core.ContactLocations).Location.MetropolitanRegion = ‘New York’“ );

Having a string-based query language is fine for deterministic queries that are well-known at compile time, but generating them programmatically is a pain. Anyone who’s done non-trivial dynamic SQL knows this. You end up writing these classes that do code generation by assembling bits and pieces of many different strings, and until you’re very familiar with the target language it can be difficult to make sure your code generator always build syntactically valid code (or, in this case, syntactically valid OPath queries).

Given the potential complexity of a meaningful OPath query, are we going to need something analogous to CodeDOM to help us build them correctly? I'm not suggesting that we need something nearly as big or complex as CodeDOM -- just that it would be nice to have an API that consisted of a little bit more than StringBuild.Append().

CodeDOM, while being at times verbose and laborious, has a nice side effect: If you’re writing “pure” CodeDOM (i.e., no CodeSnippetExpressions), you can virtually guaranteed that the code generated from that CodeDOM graph will compile. Given the option, I’d rather debug compiler errors in my code generator than in my generated code, and CodeDOM buys me that. It would be nice to have something similar for OPath.

Here’s a potentially scary thought around generating OPath queries programmatically: Are we opening up a potential attack surface here, introducing the potential for “SQL injection”-like vulnerabilities into our object models?

 

Sunday, November 23, 2003 11:25:05 AM (Pacific Standard Time, UTC-08:00)  #    Comments [3]
 Thursday, November 13, 2003

Eric Gunnerson presents an interesting pattern that happens to get around the problems with generics, operators, and primitive types that I mention in this rant. The implementation he presents is quite clever.

His code demonstrates a property of generic methods of which I was not previously aware: namely, you can override generic methods with non-generic methods, provided the compiler has enough information to close the type of the generic method which you are overriding. From Eric's example (code's his, comments are mine):

//The abstract generic root class
public abstract class Calculator
{
    public abstract T Add(T a, T b);
}

//The concrete non-generic inheritor
public class Calculator: Calculator{
   //Note that this signature matches the signature
   //of the closed form of the abstract method inherited
   //from the parent class, even though the overriding
   //method is not itself generic.

   public override int Add(int a, int b)
   {
            return a + b;
   }
}

As Eric points out, this will never be as fast as something like this:

public T Add(T a, Tb) where T:#IPromiseThatWhatEverIPutInHereWillBeAddableJustTrustMe#

due to that his example has the overhead of an additional vtable dispatch. However, his example has the distinct advantage of actually working, since the concept between the # marks is at present inexpressable in C# (or the CLR, for that matter).

The ability to do this seems a little strange to me, because it sort of implies that generic params are not a true component of a method signature. Apparently, they're not used to do vtable dispatch, because his example is showing that the generic T Add() and the non-generic int Add(int) are actually occupying the same slot in the vtable. Or has the CLR's vtable dispatch algorithm become smart enough to close the generic and then do the dispatch against that result, regardless of whether the target method is actually generic or not. Eric's example seems to imply the latter. I think the MSR whitepaper touched on this a little bit, but if I remember correctly, this fell under the section of “forthcoming publication“...

Anyway, I think that it's very important that C# generics be able to support some sort of generic algorithm capability. Even if that support comes in the somewhat roundabout way that Eric presented, it's better than nothing. Yes, I love generic collection type and will sing the praises of the Whidbey implementation if for no other reason than that, but you can do some very, very cool things with generic algorithms...

 

Thursday, November 13, 2003 9:55:23 PM (Pacific Standard Time, UTC-08:00)  #    Comments [3]
 Tuesday, November 11, 2003

One of the big justifications for generics I heard at the PDC was that generics make operations on primitive types much faster, because they eliminate boxing/unboxing overhead. Anders Hejilsberg did a demo comparing the relative performance of List<int> vs an ArrayList containing ints, and the generic was almost twice as fast.

However, it seems like generics are almost useless for doing more interesting things on primitive types (like combining them!). Take the following simple function:

public T Add<T> ( T t1, T t2 ){ return t1 + t2; }

This function will not compile. You get a compiler error that says something like “Cannot apply operator '+' to types T and T“.

This is a reasonable error. The Official Way Around this class of problems is to use type constraints to tell the compiler the set of operations that your generic expects its generic parameters to support. This works great when T is complex object -- presumably, you have the freedom to define an operational interface and constrain your generic in terms of that interface. However, this doesn't work with primitive types. How, using type constraints, am I supposed to express “the set of operations common to primitive numeric types“ when those primitive numeric types don't implement a common interface precisely because they are primitive?

I suppose you could solve this problem by boxing your primitive ints into objects that implement an operation interface, but this solution negates any potential performance gains that a generic solution would give you.

Why isn't there a formalization of the set of operations common to numeric types?

Tuesday, November 11, 2003 10:20:49 AM (Pacific Standard Time, UTC-08:00)  #    Comments [6]
 Friday, November 07, 2003

I didn't get to make it to any of the IDE talks at the PDC, but I'm sort of surprised that there haven't been more people talking about extending the Whidbey IDE.

Now that the IDE has support for refactoring, I'm starting to get really excited about extending the Visual Studio IDE. The reason why: in order to do Refactoring-type code transformation, the IDE has to do a lot of work behind the scenes to maintain an abstract syntax tree representation of your current code. Most refactoring implementations I've seen ( take C# Refactory, for example ) have both a lexer and a parser that can be invoked on demand. I would imagine that Whidbey does much the same thing -- the first phase of any refactoring transformation being a rudimentary lexing and parsing of the source tree to build up an AST which the refactoring code can then manipulate.

This has some very, very interesting possibilities for in-project code generation. I'd love to be able to write a macro that said “give me all the classes in my solution that are decorated with [MyAttribute]” and then generate some code based upon those class definitions. I'm imagining something sort of like reflection, but something that reflected over source code rather than compiled code. Coupling the ability to generate source code on demand (which the IDE already has via macros) with the ability to interrorgate exisiting source code could lead to some very, very powerful things.

To anyone who has more information than I do: will Whidbey let me build custom refactorings?

Friday, November 07, 2003 10:00:43 AM (Pacific Standard Time, UTC-08:00)  #    Comments [0]
 Tuesday, November 04, 2003

James A. Robertson over at Cincom Smalltalk saw my previous post on generic reflection in C# was a bit suprised at how verbose my code was. He posted a few lines of SmallTalk that did the same thing, and said something that amounted to “See, SmallTalk is better. Those curly-brace fanatics sure are living in the dark ages!”

Well, to be fair to C# and us proud members of the curly-brace crowd, reversing a dictionary was not the real point of the example code that I posted. I wrote that chunk of code as an illustration of how the Refelction API could manipulate generic type descriptors, not to show how use generics in code. A more “real” version of ReverseDictionary would look like:

public Dictionary ReverseDictionary( Dictionary orig )
{
   Dictionary reversed = new Dictionary();

   foreach( K key in orig.Keys )
   {
       V val = orig[key];
       reversed[val] = key;
   }

   return reversed;
}

That's hardly a “page worth of code“.

<RANT>
It bothers me when people try to start language holy wars without even reading a piece of code closely enough to understand what that code was trying to demonstrate. I thought that I made that fairly clear in my post, but apparently not. It's just frustrating to see someone go out and berate a language simply because they saw some code and didn't read the surrounding context enough to understand what was going on.

Actually, the real reason I'm pissed is because he incorrectly called me Steve Maines when he linked to me. My last name is Maine. Singular. Like the state.
</RANT>

Update: James was nice enough to fix the misspelling of my name. Rant retracted.

Tuesday, November 04, 2003 9:57:36 AM (Pacific Standard Time, UTC-08:00)  #    Comments [0]
 Sunday, November 02, 2003

I wrote my "hello, world" program for .NET 2.0. I wanted to see how reflection worked with generic types, so I wrote some code that reverses a dictionary. That is, it takes a Dictionary that maps keys to values and returns a Dictionary whose keys are the original values and the values are the original keys.

First off, some definitions: Generic types come in two flavors, open and closed. An open generic type is what you get when your write a definition for a generic type or a generic method. An open generic has as part of its definition placeholders for types that will get filled in later when the type is instantiated (much like the way a regular method has some parameters whose values get filled in when that method is actually called). A closed generic type is an instantiation of an open generic type, which is what you get when you actually bind values to an open generic. It's what you get when you declare a variable to be a List -- List is a closed generic type. Open generics are a sort of metaclass; open generics get instantiated into closed generics which get instantiated into objects.

Ok, enough theory. There's some significant additions to the Reflection API's to support generics in Whidbey. First off, System.Type and System.Reflection.MethodInfo now support a property called HasGenericParameters, which returns true if a type or method is generic. There's also a related property called HasUnboundGenericParameters which will tell you if a type is an open or closed generic.

The only way you can actually get your hands on an open generic type at runtime is to call the GetGenericTypeDefinition() method on the type of the closed generic. This is how you can convert from typeof( Dictionary ) to typeof( Dictionary ).

Given a closed generic type, you can determine the types to which the generic parameters are bound by calling the GetGenericParameters() method. This gives you back an array of System.Type instances that are positionally related to the types of the parameters. So if you do a GetGenericParameters on typeof( SomeClass) you'll get back an type array of

{ typeof( int ), typeof( string ), typeof( float ) }.

You can convert an open generic to a closed generic by calling BindGenericParameters() and passing it a type array. Like GetGenericParameters, this array is parallel to the generic parameter list. Thus, you can convert a typeof( SomeClass< T, U, V > ) to a typeof( SomeClass< int, string, float > ) by calling BindGenericParameters( new Type[]{ typeof(int), typeof(string), typeof(float) } ).

Once you have a closed generic type hanging around, you can get an instance of that closed generic in the normal reflection way -- by calling Activator.CreateInstance(). Nothing magical here.

There are a couple of other methods hanging around System.Type that have to do with generics, but I didn't have any immediate use for them.

In summary:

   -- System.Type and System.MethodInfo now have a HasGenericParameters property
   -- You can convert can convert a closed generic to an open generic by calling GetGenericTypeDefinition()
   -- You can convert an open generic to a closed generic by calling BindGenericParameters()
    -- You can instantiate a closed generic with Activator.CreateInstance()

Here's the code for ReverseDictionary():

public static Dictionary ReverseDictionary( Dictionary original )
{
   Type originalType = original.GetType();
  
Type openOriginal = originalType.GetGenericTypeDefinition();
  

   Type[] typeParameters = originalType.GetGenericParameters();
   Debug.Assert( typeParameters.Length == 2 );

   Type keyType = typeParameters[0];
  
Debug.Assert( false == keyType.IsGenericParameter );

  
Type valueType = typeParameters[1];
  
Debug.Assert( false == valueType.IsGenericParameter );

  
Type reverseDictType = openOriginal.BindGenericParameters( new Type[]{ valueType, keyType } );
  
Debug.Assert( true == reverseDictType.HasGenericParameters );
  
Debug.Assert( false == reverseDictType.HasUnboundGenericParameters );

   Dictionary reverse = (Dictionary)Activator.CreateInstance( reverseDictType );

   //We could have just said
  
//Dictionary reverse = new Dictionary()
  
//but that wouldn't have made for a very fun demo...

   foreach (K key in original.Keys)
  
{
    
V val = original[key];
    
reverse[val] = key;
   
}

  
return reverse;
}

 

GenericReflection.cs.txt (2.37 KB)
Sunday, November 02, 2003 2:24:43 AM (Pacific Standard Time, UTC-08:00)  #    Comments [0]