In What
does this code print? Eric Gunnerson shows this C# code
(int
derives from object
in C#):
using System; public class A { public void F(int i) { Console.WriteLine("A: {0}", i); } } public class B : A { public void F(object o) { Console.WriteLine("B: {0}", o); } } public class Test { public static void Main() { B b = new B(); b.F(1); } }
And the answer B: 1
surprised me. So I tried the
equivalent in Java (three source files)
public class A { public void f(Integer i) { System.err.println("A: " + i.toString()); } } public class B extends A { public void f(Object o) { System.err.println("B: " + o.toString()); } } public class Test { public static void main(String[] args) { B b = new B(); b.f(new Integer(1)); } }
and it printed A: 1
. What I expected.
Before I dig deeper into the Java case, let's see why the C#
version works the way it does. Eric explains
that method dispatch at runtime will occur on the most derived class
that holds a method that could satisfy a certain method invocation and
then will search for the best match in that class. So here the
runtime will find that F
in B
does match the
invocation in Test
and is never going to look at
A
.
The reason for this behavior, he says, is versioning. If you
invoke any method on B
and some time later anybody
introduces a new method into A
that would better match
your invocation the runtime will still choose B
's
method.
So how does it work in Java? Sometimes I enjoy digging into the JLS,
sick, I know. Java performs much of the method resolution at compile
time. At compile
time it will determine which method signature matches a certain
invocation best - and will select A
- and at
runtime it will use the method in the most derived class that has
the same signature which was determined at compile time.
This has interesting consequences. If you remove the definition
of f
from A
and recompile everything, the
the code will (certainly) print B: 1
. Now add back
f
in A
and only recompile A
but
not Test
, it still prints B: 1
. Only if you
recompile Test
as well, it will return to A:
1
.
In a way Java solves the versioning problem, but only as long as
you don't recompile your code against a newer version of
A
. While C#'s way is counterintuitive (to a seasoned
Java developer, at least), it is more predictable at the same time.
I'm not sure which way I prefer.
path: /en/dotNet | #
In the past I've been using cc-mode and just told it to use C++ for my C# coding on Linux or my Mac. Not terrific, but it worked.
A post on the Mono developer list pointed to http://mfgames.com/linux/csharp-mode and Dylan Moonfire followed up with a comment that he had just released version 0.4.0. It seems to play well with XEmacs (different other versions I tried all failed for me). Looks like I finally get "internal" highlighted as a keyword.
path: /en/dotNet/XEmacs | #
My "msbuild" search feed today made me discover Waaargh.NET by Stéphane Tombeur that holds some interesting posts. In particular I liked the Creating custom MSBuild tasks and CruiseControl.NET and MSBuild posts.
The first one explains how to write an MSBuild task by converting a NAnt task - which turns out to be mostly a matter of six simple steps. I assume that it would get more complex for more elaborated tasks, but this really looks rather straight forward.
The second one comes with an MSBuilder based on NAntBuilder that can be used to drop into CruiseControl.NET and you are done. Nice work, I hope it finds its way into cc.net.
path: /en/dotNet/msbuild | #
Mitch Denny has a little history lesson on MSBuild (via Jomo Fisher).
I certainly have to disagree with
Over the years, as Ant got more popular the beautiful target/depedency model got poluted by stinking proceduralistsI doubt that MSBuild is less procedural than a proper Ant build file, but that's just me.
The very idea of items in MSBuild - tasks, targets and even projects declaring their inputs and outputs, which would allow pipes of tasks or projects to depend of the outputs of other projects - still has a lot of appeal to me.
path: /en/dotNet/msbuild | #
Octavo:/tmp bodewig$ cat > build.xml <project> <echo>os.name = ${os.name}</echo> <echo>java.home = ${java.home}</echo> <echo>ant.java.version = ${ant.java.version}</echo> <echo>java.vendor = ${java.vendor}</echo> <echo>java.version = ${java.version}</echo> </project> ^D Octavo:/tmp bodewig$ ant Buildfile: build.xml [echo] os.name = Mac OS X [echo] java.home = /System/Library/Frameworks/JavaVM.framework/Versions/1.4.2/Home [echo] ant.java.version = 1.4 [echo] java.vendor = Apple Computer, Inc. [echo] java.version = 1.4.2_05 BUILD SUCCESSFUL Total time: 2 seconds Octavo:/tmp bodewig$ JAVACMD=/usr/bin/mono ANT_OPTS=/Users/bodewig/ikvm/bin/ikvm.exe ant Unable to locate tools.jar. Expected to find it in /Users/bodewig/ikvm/bin/lib/tools.jar Buildfile: build.xml [echo] os.name = Unix 7.8.0.0 [echo] java.home = /Users/bodewig/ikvm/bin [echo] ant.java.version = 1.5 [echo] java.vendor = Jeroen Frijters [echo] java.version = 1.4 BUILD SUCCESSFUL Total time: 23 seconds
More experiments to come.
The difference between in java.version
and ant.java.version
means, GNU CLASSPATH already has java.lang.Readable
path: /en/dotNet/ikvmnet | #