Proving a Point with LINQ (or, the Longest Type Names in the .NET Framework)
I will often take a few moments during a presentation to get a chuckle by bringing up the SharedConnectionWorkflowCommitWorkBatchService. Due to the sheer length of the type name, it's sure to bring a smile or two in one of my otherwise snore-inducing presentation.
The other day, while reviewing a deck for a colleague I brought up that he should mention this type, and that it usually gets a laugh from a developer audience. He then asked, "Is that really the longest type name in the framework?" My response, "Well, I'm not really sure, I bet you could do some LINQ magic in order to figure it out."
Well, sitting on the bus this morning with some traffic time to spare (and needing to work on my LINQ skills a bit), put together some code. My goal was to go through all of the types, calculate their length and figure out which one was the longest named type. You can replicate this, create a new console application, right click to add references, and select all the references you want to add into this comparison. I added all of the system.* assemblies, PresentationFramework, PresentationCore and WindowBase. You could do even more by selecting everything in the reference dialog.
The thing that appeals to me about LINQ is really that i can just write a query, I don't have to worry about looping through a number of times. I also like the fact I can develop by refining, that is, I can get a collection back, tweak the query to shape it, take that new collection and query against that.
Let's first look at what I started with:
var orderedTypeListing =
(from assembly in AppDomain.CurrentDomain.GetAssemblies()
select assembly.GetExportedTypes());
This ends up returning an object of type IEnumerable<List<Type>>, which doesn't quite get me to what I want.
I use the Aggregate extension method to operate on this and create an entire list of all the types contained (think of this as flattening the tree by one level, or only returning the leaf nodes in the tree). With the Aggregate method, I need to provide the operation to do the accumulation.
var orderedTypeListing =
(from assembly in AppDomain.CurrentDomain.GetAssemblies()
select assembly.GetExportedTypes()).Aggregate(
(x, y) =>
{ return x.Union(y).ToArray(); }
)
The only weirdness here is that I have to use ToArray(); since Aggregate returns T[], rather than IEnumerable<T>.
Now I add a little bit of code to do some ordering. I like the ordering syntax since I just provide a way to get the value I want to sort by. Remember to use OrderByDescending in order to get the right ordering.
Our query near looks like this:
var orderedTypeListing =
(from assembly in AppDomain.CurrentDomain.GetAssemblies()
select assembly.GetExportedTypes()).Aggregate(
(x, y) =>
{ return x.Union(y).ToArray(); }
).OrderByDescending(
(x) =>
{ return x.Name.Length; }
);
So this returns a list, ordered by the length of the name.
I could just grab the first one off the list, and I'd be done, but I'd like to see the 100 longest names, just in case I'm wrong about our friend SharedConnectionWorkflowCommitWorkBatchService. To do this, I'll say I want to take 100 off the top of the list and print those out.
int count = 1;
orderedTypeListing.Take(100).ToList().ForEach
(
x =>
Console.WriteLine("{0}: {1}, {3} ({2})",
count++,
x.Name.Length,
x.Assembly.FullName.Split(",".ToCharArray())[1],
x.Name)
);
This will pretty print the top 100 longest names (these are at the bottom of the post).
Next, i thought, I'd really like to see a histogram of the sizes. To do that, I need to group by data. The Group() extension method, just like the order method, allows me to pass in the value by which I want to group (and that can be computed, say a hash). It also returns an IGrouping that lets me see the key (in my case, the size of the key) and the IEnumerable itself.
var groupedTypeListing = orderedTypeListing.GroupBy(
t =>
{ return t.Name.Length; }
);
groupedTypeListing.ToList().ForEach(
x =>
Console.WriteLine("{0} : {1}", x.Key, x.Count())
);
Finally, I thought I'd see just how many types were included in each version of the framework, and prove that I can group by anything, including manipulating the string on the assembly's full name.
var groupedVersionListing = orderedTypeListing.GroupBy(
t =>
{ return t.Assembly.FullName.Split(",".ToCharArray())[1]; }
);
groupedVersionListing.ToList().ForEach(
x =>
Console.WriteLine("{0} : {1}", x.Key, x.Count()
)
);
This has now let me retrieve a lot of interesting data (interesting at least to me :-) ) that I was able to write with a pretty simple query syntax and do some somewhat sophisticated things (grouping, etc).
It turns out that our friend SharedConnectionWorkflowCommitWorkBatchService is only number 16 on my list. The real honor goes to:
AttachedPropertyBrowsableWhenAttributePresentAttribute, with 53 characters.
Histogram of Type Name Sizes
Top 100 Longest Type Names
Rank | Length | Name | Version |
1 | 54 | AttachedPropertyBrowsableWhenAttributePresentAttribute | v3.0 |
2 | 53 | ListViewVirtualItemsSelectionRangeChangedEventHandler | v2.0 |
3 | 52 | ServiceModelEnhancedConfigurationElementCollection`1 | v3.0 |
4 | 50 | DataGridViewCellContextMenuStripNeededEventHandler | v2.0 |
5 | 50 | ListViewVirtualItemsSelectionRangeChangedEventArgs | v2.0 |
6 | 49 | DataGridViewRowContextMenuStripNeededEventHandler | v2.0 |
7 | 49 | WorkflowServiceAttributesDynamicPropertyValidator | v3.5 |
8 | 48 | DataGridViewColumnDividerDoubleClickEventHandler | v2.0 |
9 | 47 | DataGridViewCellContextMenuStripNeededEventArgs | v2.0 |
10 | 47 | DataGridViewCellStyleContentChangedEventHandler | v2.0 |
11 | 47 | ReadOnlyActiveDirectorySchemaPropertyCollection | v2.0 |
12 | 47 | ExtendedWorkflowRuntimeServiceElementCollection | v3.5 |
13 | 46 | IDataGridColumnStyleEditingNotificationService | v2.0 |
14 | 46 | DataGridViewRowContextMenuStripNeededEventArgs | v2.0 |
15 | 46 | UpdateProgressAssociatedUpdatePanelIDConverter | v3.5 |
17 | 46 | DispatcherUnhandledExceptionFilterEventHandler | v3.0 |
18 | 45 | DataGridViewColumnDividerDoubleClickEventArgs | v2.0 |
19 | 45 | DataGridViewCellToolTipTextNeededEventHandler | v2.0 |
20 | 45 | DataGridViewEditingControlShowingEventHandler | v2.0 |
21 | 45 | DataGridViewRowDividerDoubleClickEventHandler | v2.0 |
22 | 45 | NamedServiceModelExtensionCollectionElement`1 | v3.0 |
23 | 45 | StandardBindingOptionalReliableSessionElement | v3.0 |
24 | 45 | X509CertificateTrustedIssuerElementCollection | v3.0 |
25 | 45 | X509ScopedServiceCertificateElementCollection | v3.0 |
26 | 45 | InitiatorServiceModelSecurityTokenRequirement | v3.0 |
27 | 45 | RecipientServiceModelSecurityTokenRequirement | v3.0 |
28 | 45 | DataContractSerializerMessageContractImporter | v3.0 |
29 | 45 | ClientWindowsAuthenticationMembershipProvider | v3.5 |
30 | 45 | IClientFormsAuthenticationCredentialsProvider | v3.5 |
31 | 45 | AttachedPropertyBrowsableForChildrenAttribute | v3.0 |
32 | 44 | DataGridViewCellStyleContentChangedEventArgs | v2.0 |
33 | 44 | DataGridViewColumnDesignTimeVisibleAttribute | v2.0 |
34 | 44 | CodeParameterDeclarationExpressionCollection | v2.0 |
35 | 44 | ReadOnlyActiveDirectorySchemaClassCollection | v2.0 |
36 | 44 | ServiceModelConfigurationElementCollection`1 | v3.0 |
37 | 44 | UseManagedPresentationBindingElementImporter | v3.0 |
38 | 44 | WS2007FederationHttpBindingCollectionElement | v3.0 |
39 | 43 | KeyContainerPermissionAccessEntryCollection | v2.0 |
40 | 43 | KeyContainerPermissionAccessEntryEnumerator | v2.0 |
41 | 43 | DataGridViewAutoSizeColumnsModeEventHandler | v2.0 |
42 | 43 | DataGridViewCellErrorTextNeededEventHandler | v2.0 |
43 | 43 | DataGridViewRowHeightInfoNeededEventHandler | v2.0 |
44 | 43 | DataGridViewRowHeightInfoPushedEventHandler | v2.0 |
45 | 43 | PerformanceCounterPermissionEntryCollection | v2.0 |
46 | 43 | TypeUniqueIdentifierSchemaImporterExtension | v2.0 |
47 | 43 | IRemoteArgumentDictionaryEnumeratorContract | v2.0 |
48 | 43 | ForeignKeyReferenceAlreadyHasValueException | v3.5 |
49 | 43 | SecurityPackageContextConnectionInformation | v2.0 |
50 | 43 | PrintSystemObjectPropertiesChangedEventArgs | v3.0 |
51 | 43 | IssuedTokenClientBehaviorsElementCollection | v3.0 |
52 | 43 | IssuedTokenParametersEndpointAddressElement | v3.0 |
53 | 43 | X509ServiceCertificateAuthenticationElement | v3.0 |
54 | 43 | TransportConfigurationTypeElementCollection | v3.0 |
55 | 43 | ClientFormsAuthenticationMembershipProvider | v3.5 |
56 | 43 | ServiceDescriptionFormatExtensionCollection | v2.0 |
57 | 43 | DispatcherUnhandledExceptionFilterEventArgs | v3.0 |
58 | 42 | DataGridViewCellToolTipTextNeededEventArgs | v2.0 |
59 | 42 | DataGridViewEditingControlShowingEventArgs | v2.0 |
60 | 42 | DataGridViewAutoSizeColumnModeEventHandler | v2.0 |
61 | 42 | DataGridViewColumnStateChangedEventHandler | v2.0 |
62 | 42 | DataGridViewRowErrorTextNeededEventHandler | v2.0 |
63 | 42 | DataGridViewRowDividerDoubleClickEventArgs | v2.0 |
64 | 42 | ToolStripItemDesignerAvailabilityAttribute | v2.0 |
65 | 42 | EdmRelationshipNavigationPropertyAttribute | v3.5 |
66 | 42 | BehaviorServiceAdornerCollectionEnumerator | v2.0 |
67 | 42 | DirectoryServicesPermissionEntryCollection | v2.0 |
68 | 42 | ForestTrustRelationshipCollisionCollection | v2.0 |
69 | 42 | X509ClientCertificateAuthenticationElement | v3.0 |
70 | 42 | ServiceControllerPermissionEntryCollection | v2.0 |
71 | 42 | DatabaseNotEnabledForNotificationException | v2.0 |
72 | 42 | DesignTimeResourceProviderFactoryAttribute | v2.0 |
73 | 41 | CryptographicUnexpectedOperationException | v2.0 |
74 | 41 | DataGridPreferredColumnWidthTypeConverter | v2.0 |
75 | 41 | IDesignTimeResourceProviderFactoryService | v2.0 |
76 | 41 | SiteMapDesignerHierarchicalDataSourceView | v2.0 |
77 | 41 | WindowsUserNameSecurityTokenAuthenticator | v3.0 |
78 | 41 | PrintSystemObjectPropertyChangedEventArgs | v3.0 |
79 | 41 | ConnectionOrientedTransportBindingElement | v3.0 |
80 | 41 | SecurityContextSecurityTokenAuthenticator | v3.0 |
81 | 41 | SecureConversationSecurityTokenParameters | v3.0 |
82 | 41 | X509CertificateInitiatorServiceCredential | v3.0 |
83 | 41 | X509CertificateRecipientServiceCredential | v3.0 |
84 | 41 | RecordDescriptionToTypeReferenceConverter | v3.0 |
85 | 41 | BlobMessageEncodingBindingElementImporter | v3.0 |
86 | 41 | DistributedTransactionPermissionAttribute | v2.0 |
87 | 41 | CompositeActivityDesignerLayoutSerializer | v3.0 |
88 | 41 | SqlPersistenceWorkflowInstanceDescription | v3.0 |
89 | 41 | AttachedPropertyBrowsableForTypeAttribute | v3.0 |
90 | 40 | DataGridViewAutoSizeColumnsModeEventArgs | v2.0 |
91 | 40 | DataGridViewCellErrorTextNeededEventArgs | v2.0 |
92 | 40 | DataGridViewCellStateChangedEventHandler | v2.0 |
93 | 40 | DataGridViewRowHeightInfoNeededEventArgs | v2.0 |
94 | 40 | DataGridViewRowHeightInfoPushedEventArgs | v2.0 |
95 | 40 | ListViewItemSelectionChangedEventHandler | v2.0 |
96 | 40 | DesignerSerializationVisibilityAttribute | v2.0 |
97 | 40 | TypeSmallDateTimeSchemaImporterExtension | v2.0 |
98 | 40 | SchemaImporterExtensionElementCollection | v2.0 |
99 | 40 | ProtectedConfigurationProviderCollection | v2.0 |
100 | 40 | DirectoryAttributeModificationCollection | v2.0 |
Comments
- Anonymous
April 30, 2008
PingBack from http://microsoftnews.askpcdoc.com/linq/proving-a-point-with-linq-or-the-longest-type-names-in-the-net-framework