Bugs?
Ho scoperto una cosa a mio parere strana: un metodo (System.Web.UI.WebControls.ParameterCollection.Add(String, DbType, String)) che esiste solo nelle versioni service pack del framework .NET (2.0 SP2, 3.0 SP2, 3.5 SP1) ma non nelle versioni "normali". Mi chiedo come mai se il metodo e' stato introdotto in .NET 2.0 SP2, l'abbiano tolto dalle .NET 3.0 e .NET 3.0 SP1 per reintrodurlo nella .NET 3.0 SP2 per poi toglierlo di nuovo dalla .NET 3.5 e finalmente reintrodurlo nella .NET 3.5 SP1???...
[OT] Per i tanti amici milanesi: sono a Milano dall'8 di marzo e torno in Romania settimana prossima, probabilmente mercoledi' - se vi fa...
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...
Se vogliamo che il nostro codice giri anche su Mono, dobbiamo utilizzare:
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "aaa.bbb")
al posto di:
AppDomain.CurrentDomain.BaseDirectory + "aaa.bbb"
perche' su Mono, BaseDirectory ritorna una stringa che non finisce in Path.DirectorySeparatorChar, mentre su CLR si'. E questo va anche in generale, quando costruiamo il path da piu' pezzi, non solo nel caso della BaseDirectory. Per esempio, chi utilizza fyiReporting RDL Project su Mono, dovrebbe modificare la riga 81 nel file Runtime/RdlEngineConfig.cs nei sorgenti del progetto e ricompilare, da:
file = dir + "RdlEngineConfig.xml";
a:
file = Path.Combine(dir, "RdlEngineConfig.xml");
perche' la stringa dir, per come e' stata costruita, su CLR finisce in Path.DirectorySeparatorChar, mentre su Mono no. In...
Bella sorpresa trovare oggi nella casella postale una gentilissima risposta da parte di Joe Duffy, Program Manager del team del CLR, a due mail che gli avevo inviato l'agosto scorso (vedi il post "I delegate sono struct???"):
"Adrian, first I want to apologize that this mail was not responded to sooner. I found it in my “junk mail” folder for some reason.Next, better late than never I suppose! Thank you very much for pointing both of these errors out, and also for the kind words about the book.You are of course right in both cases. I honestly couldn’t believe I wrote...
Il messaggio dell'errore CS0310 (e anche la sua descrizione) secondo me è incompleto, cioè non basta che un tipo abbia un costruttore pubblico senza parametri per poter essere utilizzato come type parameter in un tipo generico con una constructor constraint. Il messaggio dell'errore dice: "The type 'typename' must have a public parameterless constructor in order to use it as parameter 'parameter' in the generic type or method 'generic'".
Controesempio:
abstract class Foo{ public Foo() { }} class Bar<T> where T : new() { }
Il tipo Foo ha un costruttore pubblico senza parametri ma l'espressione new Bar<Foo>() non compila (error CS0310).
La frase nelle...
Per chi ha voglia di un pizzico di surreale, ecco uno snippet che mi ha fatto impazzire...
// foo.cs//// csc /d:DUMMY foo.cs # stampa False // csc foo.cs # stampa True using System;using System.Diagnostics; class Test{ [Conditional("DUMMY")] static void Dummy(double x) { x = x; } static void Main() { double x = Math.Pow(double.Epsilon, 2); Dummy(x); Console.WriteLine(0 < x && x < double.Epsilon); }}
Questo codice stampa False se compilato col simbolo DUMMY e True in caso contrario, cioè l'espressione:
0 < x && x < double.Epsilon
ha valori diversi in base al fatto che Dummy (che in teoria non dovrebbe far nulla) venga...
Se l'attributo System.ObsoleteAttribute avesse il target anche su AttributeTargets.Assembly, si eviterebbe di decorare tutti i tipi di un assembly interamente obsoleto come Obsolete.
Per esempio, nella Beta 2, tutti i tipi dell'assembly Microsoft.VisualC.dll sono decorati con [Obsolete(”Microsoft.VisualC.dll is an obsolete assembly and exists only for backwards compatibility.”)]. Potremmo cosi' avere, piu' semplice, una volta sola per l'intero assembly:
[assembly:Obsolete(”Microsoft.VisualC.dll is an obsolete assembly and exists only for backwards compatibility.”)]
A me, il fatto che il seguente snippet stampi:
TrueTrueFalse
a console, risulta stranissimo!
using System;using System.ComponentModel; class Test{ static void Main() { BindableAttribute a = BindableAttribute.Default; BindableAttribute s = new BindableAttribute(BindableSupport.Default); Console.WriteLine(a.IsDefaultAttribute()); // True Console.WriteLine(s.IsDefaultAttribute()); // True Console.WriteLine(a.Equals(s)); // False??? }}
Praticamente, l'enumeration System.ComponentModel.BindableSupport, così com'è definita nel framework:
namespace System.ComponentModel{ public enum BindableSupport { No, Yes, Default // 2??? }}
vede il valore dell'elemento Default come 2 e non come 0 (che è quello di No). In effetti, nella definizione della classe System.ComponentModel.BindableAttribute, che wrappa l'enumeration di sopra, abbiamo:
namespace System.ComponentModel{ // ... public sealed class BindableAttribute : Attribute { public static readonly BindableAttribute...
Ieri sono uscite le specifiche per la versione 8.0 (Beta 2) di Visual Basic .NET. Purtroppo, anche nella nuova versione, ho ritrovato gli stessi errori segnalati e confermati per la versione 7.1.
Devo dire che non esiste proprio paragone tra la qualità delle specifiche di C# e quelle di VB .NET e non capisco bene perché.
Non sempre quando vi appare una finestra "Just-In-Time Debugging", nel messaggio "An exception 'MyNamespace.MyException' has occurred in MyApplication.exe", l'eccezione debba essere una exception. Può capitare di incontrare al posto di 'MyNamespace.MyException', 'Launch for user' :-)
Un esempio, in questo snippet:
class Foo{ static void Main() { System.Diagnostics.Debugger.Break(); }}
Secondo me, crea un po' di confusione il messaggio.
In SLAR alla pagina 389 c'è scritto che:
"This class is used only by the system; applications cannot create instances of the System.Security.SecurityElement type"
Questa affermazione non è vera: la classe SecurityElement è in sostanza un parser XML leggero (sa parsare solo elementi, attributi e testo) e può essere utilizzata come qualunque altra classe. Un bellissimo esempio è mostrato in questo post di Shawn Farkas sulla trasformazione SecurityElement <-> XmlElement. Lo dimostra anche l'esempio che si trova in SLAR nella stessa pagina (la classe Samples.SecurityElementSample).
Visto che non ricevo tutti i giorni una mail da Paul Vick, la posto :-)
Adrian,
Thank you so much for your email! You are correct that the examples are wrong... it looks like they somehow slipped through our editing process!I'll make sure that they are corrected in the next version of the specification, which should be coming out with Beta2 of VB 2005.
Thanks so much for the bug reports!
Paul
La mail conferma i bug segnalati in un post precedente. Gli indirizzi MSDN degli esempi errati sono:
9.2.6 Event Handling
9.6.2 WithEvents Variables
10.5.1 RaiseEvent Statement
Il seguente snippet è preso dal paragrafo 9.6.2 delle specifiche VB .NET:
Imports SystemClass Raiser Public Event Constructed() Public Sub New() RaiseEvent Constructed() End SubEnd ClassModule Test Private WithEvents x As Raiser Private Sub HandleConstructed() Handles x.Constructed Console.WriteLine("Constructed") End Sub Public Sub Main() x = New Raiser End SubEnd Module
Non mi spiego come un errore così elementare (vedi la Reference: "Non-shared events should not be raised within the constructor of the class in which they are declared. Although such events do not cause runtime errors, they may fail to be caught by associated event handlers") possa essere fatto proprio da...
Mi sembra contraddittorio avere per una classe sealed una security action InheritanceDemand...
Succede così per la classe Executor:
namespace System.CodeDom.Compiler{ [PermissionSet(SecurityAction.LinkDemand, Name="FullTrust")] [PermissionSet(SecurityAction.InheritanceDemand, Name="FullTrust")] public sealed class Executor { // }}
La prova:
PermissionSetAttribute è uno pseudo-custom attribute, perciò troviamo InheritanceDemand come flag inheritcheck nella direttiva .permissionset.
Raf? :-)
Se la classe Foo la compiliamo separatamente in un file "Foo.vb":
' Foo.vb<Microsoft.VisualBasic.CompilerServices.StandardModule()> _Public NotInheritable Class Foo Public Sub New() End SubEnd Class
con:
vbc /t:library Foo.vb
e proviamo a consumarla nel codice seguente:
' Test.vbClass Test Shared Sub Main() Dim f As Foo = New Foo End SubEnd Class
con:
vbc /r:Foo.dll Test.vb
otteniamo l'errore BC30371 ("Module 'Foo' cannot be used as a type.") come mostrato nel post precedente.
Se invece mettiamo tutto in un file, l'errore non appare più!
' TestFoo.vb<Microsoft.VisualBasic.CompilerServices.StandardModule()> _Public NotInheritable Class Foo Public Sub New() End SubEnd ClassClass Test Shared Sub Main() Dim f As Foo = New Foo End SubEnd Class...
Il problema ormai è chiuso, dopo la raffica di post di Paolo (qui, qui, qui e qui), Cristian (qui) e Raffaele (qui, qui e qui).
Con un ritardo di una settimana, non faccio adesso altro che aggiungere due ciliegine (2 cents?) sulla torta:
Richter nel suo libro (pp. 443-444) avvertiva:
"Ogni qualvolta si ottiene un'analisi dello stack, è possibile rilevare che alcuni metodi dello stack di chiamate non sono visualizzati nell'analisi dello stack. Il motivo di questa assenza è rappresentato dal fatto che il compilatore JIT può eseguire l'inlining dei metodi per evitare l'overhead generato dalla chiamata e dalla restituzione di un metodo...
Abbiamo visto ieri, grazie all'osservazione di Nicu, che le specifiche avrebbero dovuto dire:
"Any of the static members declared by the class are referenced" al posto di:
"Any of the static members of the class are referenced".
Curioso, sono ricorso al libro di Richter e ho scoperto che lui fa la precisazione (p. 188) che manca nelle specifiche: quando parla di campo statico, aggiunge "non ereditato"! A Box invece, manca (p. 62) questa precisazione.
Ieri per tutto il giorno vi ho parlato dell'articolo di Nicu G. Fruja in cui porta alla luce "a few gaps and mistakes in the C# reference manual, inconsistencies with different implementations of C#". Gli ho scritto chiedendogli dei commenti su "The reference of the static field B.x triggers only the initialization of A" e oggi mi ha risposto così (traduco dal rumeno):
Le specifiche affermano erroneamente che:
(17.11) "The execution of a static constructor is triggered by the first of the following events to occur within an application domain:
An instance of the class is created.
Any of the static members...
Un po' di giorni fa segnalavo il fatto strano che questo codice:
class Foo{ static void Main() { System.Console.WriteLine(System.Void.Equals(null, null)); System.Console.Read(); }}
compilava (con Visual C# .NET Compiler version 7.10.3052.4) senza errori. Stasera ho scaricato e installato il ".NET Framework Version 2.0 Redistributable Package Beta 1 (x86)" uscito oggi e compilando (con Visual C# .NET Compiler version 8.00.40607.16) lo stesso codice di sopra ho ottenuto, finalmente, l'errore che aspettavo:
error CS0673: System.Void cannot be used from C# -- use typeof(void) to get the void type object.
Adesso sono più contento :-)
Possono anche sembrarvi uguali i seguenti due snippet:
// Snippet 1using System;class Foo{ static void Main() { Console.WriteLine(Void.Equals(null, null)); Console.Read(); }}
// Snippet 2class Foo{ static void Main() { System.Console.WriteLine(System.Void.Equals(null, null)); System.Console.Read(); }}
ma non lo sono per niente! :-) Mentre il primo dà l'errore che tutti si aspettano:
System.Void cannot be used from C# -- use typeof(void) to get the void type object.
il secondo scrive senza problemi un bel True a console! :-) Dovrei pensare un po' al perché...
"typeof(System.Enum).IsXXX does behave strangely, and is more that likely a bug. We'll take a look at that soon, and hopefully have a fix if it's deemed a problem"leggo nel commento di Joel Pobar (PM SSCLI) su questo post recente di Brad Abrams.
Adesso vado a fare la nanna, ho perso tutta la sera con le stranezze di typeof(System.Enum).IsXXX
Per chi lavora con Infragistics NetAdvantage 2004 Volume 1 e vuole utilizzare Infragistics.WebUI.UltraWebNavigator.UltraWebMenu non solo in maniera drag-and-drop del componente, un workaround per evitare System.NullReferenceException è questo:
sostituire la riga: string appDir = Page.Request.ServerVariables["APPL_PHYSICAL_PATH"];nel metodo: public int ReadXmlFile(string, bool, bool)della classe: Infragistics.WebUI.UltraWebNavigator.UltraWebMenu ("UltraMenu.cs")con: string appDir = System.Web.HttpContext.Current.Request.ServerVariables["APPL_PHYSICAL_PATH"];
Nel commento della classe interna System.__Filters si può leggere:
"The following are the built in filters defined for this class. These should really be defined as static methods. They are used in as delegates which currently doesn't support static methods. We will change this once the compiler supports delegates."
o in quello del costruttore static della classe System.Type:
"Because the current compiler doesn't support static delegates the _Filters object is an object that we create to contain all of the filters."
Lo so, è un'osservazione un po' da pignolo :-) ma i metodi di questa classe (System.__Filters) sono ancora instance (dal luglio 1998...) e...
Sto finendo di scrivere l'articolo sull'individuazione via reflection delle classi singleton all'interno del Framework .NET e scopro una cosa che non mi spiego: perché la classe singleton System.Reflection.Missing ha il costruttore default internal e non private? Qual è secondo voi il motivo di questa scelta? Il codice della classe lo potete guardare qua.
Un altro caso di costruttore default internal è quello della classe (anch'essa singleton) System.Empty che a differenza della precedente è una classe internal e non public.