Il comportamento del compilatore C#, presentato prima qui da Diego e poi nel mio post precedente, sembra singolare tra gli altri compilatori piu' conosciuti .NET. Il seguente snippet C# entra in stack overflow:
using System;
class Foo {
public virtual void Write(string s) {
Console.WriteLine("Foo virtual " + s);
}
}
class Bar : Foo {
public override void Write(string s) {
Console.WriteLine("Bar override " + s);
}
public void Write(string s, params string[] args) {
Write("Bar overload " + s);
}
}
class Program {
static void Main() {
Bar bar = new Bar();
bar.Write("Ciao!"); // Process is terminated due to StackOverflowException
}
}
mentre per gli altri linguaggi, stampa Bar override Ciao! Di seguito il codice equivalente in Visual Basic .NET, C++/CLI e Visual J#
Imports System
Class Foo
Public Overridable Sub Write(ByVal s As String)
Console.WriteLine("Foo virtual " + s)
End Sub
End Class
Class Bar : Inherits Foo
Public Overrides Sub Write(ByVal s As String)
Console.WriteLine("Bar override " + s)
End Sub
Public Overloads Sub Write(ByVal s As String, ParamArray args As String())
Write("Bar overload " + s)
End Sub
End Class
Module Program
Sub Main
Dim bar As Bar = New Bar
bar.Write("Ciao") ' stampa Bar override Ciao!
End Sub
End Module
using namespace System;
ref class Foo {
public:
virtual void Write(String^ s) {
Console::WriteLine("Foo virtual " + s);
}
};
ref class Bar : Foo {
public:
virtual void Write(String^ s) override {
Console::WriteLine("Bar override " + s);
}
void Write(String^ s, ... array<String^>^ args) {
Write("Bar overload " + s);
}
};
int main() {
Bar^ bar = gcnew Bar;
bar->Write("Ciao!"); // stampa Bar override Ciao!
};
import System.*;
class Foo {
public void Write(String s) {
Console.WriteLine("Foo virtual " + s);
}
}
class Bar extends Foo {
public void Write(String s) {
Console.WriteLine("Bar override " + s);
}
public void Write(String s, /** @attribute ParamArray() */ String[] args) {
Write("Bar overload " + s);
}
}
class Program {
public static void main(String[] args) {
Bar bar = new Bar();
bar.Write("Ciao"); // stampa Bar override Ciao!
}
}
Ho sempre considerato C# come linguaggio "centrale" di .NET, una chiave per capire meglio lo spirito della piattaforma - percio' mi meraviglio quando trovo comportamenti in C# che sono piuttosto l'eccezione anziche' la regola rispetto agli altri linguaggi .NET. A voi quale comportamento sembra piu' corretto/intuitivo?
Questo post di Diego Martelli, fattomi notare da un amico, riesce secondo me a sorprendere un comportamento interessante di C#, ovvero il seguente snippet di codice entra in stack overflow:
using System;
class Foo {
public virtual void Write(string s) {
Console.WriteLine("Foo virtual " + s);
}
}
class Bar : Foo {
public override void Write(string s) {
Console.WriteLine("Bar override " + s);
}
public void Write(string s, params string[] args) {
Write("Bar overload " + s);
}
}
class Program {
static void Main() {
Bar bar = new Bar();
bar.Write("Ciao!");
}
}
Probabilmente molti si aspetterebbero che venisse stampato "Bar override Ciao!" a console e invece il metodo chiamato e' il Write con l'elenco variabile di parametri e non il metodo in override. Questo comportamento e' dovuto, secondo me, al fatto che abbiamo istanziato bar come:
Bar bar = new Bar();
e non come:
Foo bar = new Bar();
Il metodo Write in override nella classe Bar non fa altro che specializzare il metodo virtuale della classe base Foo, percio' perde nella gara dei metodi in overload della classe Bar perche' Write nella sua forma espansa (ECMA-334, 14.4.2.1) ha un parametro di tipo string e zero elementi nell'array di parametri.
Detto questo, ecco il mio quiz: Cosa stampa questo snippet a console?
using System;
class Foo {
public override string ToString() {
return "override ";
}
public string ToString(params string[] args) {
return "overload ";
}
}
class Program {
static void Main() {
Foo foo = new Foo();
Console.Write(foo);
Console.WriteLine(foo.ToString());
}
}
- A: override override
- B: override overload
- C: overload overload
:-)