Chris Sells translated a Clojure implementation of a function that descends into a directory in a "lazy" manner by Craig Andera into C# but his solution is so .NET 2.0 ;-).

Here's my take

    static IEnumerable<string> GetDirectoryDescendants(string path) {
        return Directory.GetFiles(path)
            .Concat(Directory.GetDirectories(path)
                .SelectMany<string, string>(GetDirectoryDescendants)
            );
    }

where I'd hope C# 4.0's compiler will be able to deduce the types for the SelectMany itself. Interestingly the C# 3.0 compiler does fine when I use a lambda expression instead:

    static IEnumerable<string> GetDirectoryDescendants(string path) {
        return Directory.GetFiles(path)
            .Concat(Directory.GetDirectories(path)
                .SelectMany(dir => GetDirectoryDescendants(dir))
            );
    }

path: /en/dotNet | #

The GWT Tasks stuff made me work through some very old TODO items that have seriously been sitting in my inbox unanswered far too long.

More than three years ago I wrote an Ant task to be used inside NAnt or MSBuild build files and apparently some people actually use it. About a year ago Martin Harper told me that only lower case Ant properties worked but I never got around looking into it.

It turned out that I foolishly used used System.Collections.Specialized.StringDictionary to store the properties, which is not case sensitive. While I was at at it, I moved the whole class up to .NET 2.0 using generics and provided a Visual Studio solution.

Here is a new drop of source code as well as a pre-compiled DLL containing the task. The DLL has been compiled against NAnt 0.8.5 using VS 2008 - if you need any other combination (or a version not compiled against NAnt), please grab the source ZIP and compile it yourself.

The source is also available from a darcs repository:

darcs get http://stefan.samaflost.de/repos/anttask/

path: /en/dotNet | #

At first glance Generics in Java and .NET are very similar: neither of both gets anywhere close to what C++ templates can do and their inventors see the primary use in collections or holder classes (like Java's SoftReference or .NET's Nullable).

Under the covers they are very different. In Java, Generics don't really exist in your byte-code, the Java compiler knows and sees the generic types, but after that they've been "erased". In .NET on the other hand the generic type is completely known at runtime, there even are reflection methods for accessing the generic types or creating new instances of generic classes passing in a type.

As a result, the .NET implementation doesn't suffer from some of the problems erasure exhibits in Java land. Mixing of generics and varargs for example - the code below is perfectly legal:

    public class CompositeCollection<E> : ICollection<E> {
        public void AddComposited(params ICollection<E>[] colls) {
            ...
        }
        ...
    }

As a side note on Stephen Colebourne's later post, an array T[] not only implements IEnumerable<T> it even implements IList<T> in .NET 2.0.

Without erasure it is also possible to implement the same generic interface twice using different type parameters:

    class StringifiedInteger : IComparable<string>, IComparable<int> {

Yes, you can use value types (primitives) as type parameters as well, List<int> just works. Without boxing, that is.

This may sound as if the .NET generics were superior to Java's in each and every way, this is not completely true.

Part of the reason for erasure was that you could pass your type-safe generic collection classes to unsuspecting legacy code that wanted to work on plain old collections. Microsoft didn't even try to reach this point, instead they've added a new namespace System.Collections.Generic (and VS2005 will remind you of it by adding a using clause for it to each and every class you create - no, that doesn't annoy me, not at all, thank you) and ICollection<T> doesn't even inherit from ICollection.

One Java generics' feature I really miss in .NET and that isn't related to erasure at all is wildcard parameters. Stephen's CompositeCollection is a typical example for a method where wildcards would be useful. Given

    public class CompositeCollection<E> extends Collection<E> {
        public void addComposited(Collection<E> c) ...
    }

    CompositeCollection<Shape> shapes = new CompositeCollection<Shape>();

I won't be able to invoke addComposited with ArrayList<Rectangle> - neither in Java nor using the C# equivalent. In Java you'd use a wildcard type parameter

        public void addComposited(Collection<? extends E>) ...

instead and it works as intended.

Using C# you have to make the method generic itself and use what Microsoft calls a naked type constraint in the docs:

        public void AddComposited<T>(ICollection<T> c) where T : E ...

and invoke it like

            CompositeCollection<Shape> shapes = new CompositeCollection<Shape>();
            shapes.AddComposited(new List<Rectangle>());

fortunately the compiler (both Microsoft's csc as well as Mono's gmcs compiler) is smart enough to infer the method's type parameter from the argument. Still I feel the "collection of unknowns" is expressed in Java in a better way.

path: /en/dotNet | #

Using my just published Ant tasks for NAnt and MSBuild and Ant's .NET Antlib I had some fun with the various things installed on my company notebook:

$ NAnt.exe -f:pingpong.build
NAnt 0.85 (Build 0.85.1932.0; rc3; 16.04.2005)
Copyright (C) 2001-2005 Gerry Shaw
http://nant.sourceforge.net

Buildfile: file:///c:/Dokumente und Einstellungen/stefan.bodewig/AntTask/pingpong.build
Target framework: Microsoft .NET Framework 1.1
Target(s) specified: runAnt


echo:

     [echo] This is NAnt

runAnt:

[loadtasks] Scanning assembly "AntTask" for extensions.
      [ant] Buildfile: pingpong.xml
      [ant]
      [ant] echo:
      [ant]      [echo] This is Apache Ant version 1.6.5 compiled on June 2 2005 on JDK 1.5.0_05-b05
      [ant]
      [ant] runMSBuild:
      [ant] [dn:msbuild] Microsoft (R) Build Engine Version 2.0.50727.42
      [ant] [dn:msbuild] [Microsoft .NET Framework, Version 2.0.50727.42]
      [ant] [dn:msbuild] Copyright (C) Microsoft Corporation 2005. All rights reserved.
      [ant]
      [ant] [dn:msbuild] Build started 21.12.2005 20:29:57.
      [ant] [dn:msbuild] __________________________________________________
      [ant] [dn:msbuild] Project "C:\Dokumente und Einstellungen\stefan.bodewig\AntTask\pingpong.proj" (runAnt target(s)):
      [ant]
      [ant] [dn:msbuild] Target echo:
      [ant] [dn:msbuild]     This is MSBuild
      [ant] [dn:msbuild] Target runAnt:
      [ant] [dn:msbuild]     Buildfile: pingpong.xml
      [ant] [dn:msbuild]
      [ant] [dn:msbuild]     echo:
      [ant] [dn:msbuild]          [echo] This is Apache Ant version 1.7alpha compiled on December 19 2005 on JDK 1.4.2_09-b05
      [ant] [dn:msbuild]
      [ant] [dn:msbuild]     runNAnt:
      [ant] [dn:msbuild]       [dn:nant] NAnt 0.85 (Build 0.85.1932.0; rc3; 16.04.2005)
      [ant] [dn:msbuild]       [dn:nant] Copyright (C) 2001-2005 Gerry Shaw
      [ant] [dn:msbuild]       [dn:nant] http://nant.sourceforge.net
      [ant] [dn:msbuild]
      [ant] [dn:msbuild]       [dn:nant] Buildfile: file:///C:/Dokumente und Einstellungen/stefan.bodewig/AntTask/pingpong.build
      [ant] [dn:msbuild]       [dn:nant] Target framework: Mono 1.0 Profile
      [ant] [dn:msbuild]       [dn:nant] Target(s) specified: echo
      [ant] [dn:msbuild]
      [ant] [dn:msbuild]       [dn:nant] echo:
      [ant] [dn:msbuild]
      [ant] [dn:msbuild]       [dn:nant]      [echo] This is NAnt
      [ant] [dn:msbuild]
      [ant] [dn:msbuild]       [dn:nant] BUILD SUCCEEDED
      [ant] [dn:msbuild]
      [ant] [dn:msbuild]       [dn:nant] Total time: 0 seconds.
      [ant] [dn:msbuild]
      [ant] [dn:msbuild]
      [ant] [dn:msbuild]     BUILD SUCCESSFUL
      [ant] [dn:msbuild]     Total time: 1 second
      [ant] [dn:msbuild]
      [ant]
      [ant] [dn:msbuild] Build succeeded.
      [ant] [dn:msbuild]     0 Warning(s)
      [ant] [dn:msbuild]     0 Error(s)
      [ant]
      [ant] [dn:msbuild] Time Elapsed 00:00:01.24
      [ant]
      [ant] BUILD SUCCESSFUL
      [ant] Total time: 1 second
      [ant]

BUILD SUCCEEDED

Total time: 2 seconds.

This is NAnt on MS Framework 1.1 invoking Ant 1.6.5 on JDK 1.5 invoking MSBuild on MS Framework 2.0 invoking Ant's SVN trunk on JDK 1.4 (my default environment) invoking NAnt on Mono 1.1.12.

path: /en/dotNet | #

No, I'm not talking about the .NET Antlib again, this time it is the other way around. I've written my first tasks for NAnt and MSBuild and they invoke Ant.

I encountered some issues with MSBuild, mainly that I didn't find a way to define nested elements and that I can return true or false from the Execute method without any notable effect - I'll have to dig into it.

Sources, docs and binaries are available in http://stefan.samaflost.de/code/nant-msbuild-AntTask/AntTask-1.0.0.zip, please also check the archive's PGP signature (a link to my key is on the left).

path: /en/dotNet | #