luglio 2005 Blog Posts
Non mi aspettavo di trovare proprio 4 generazioni di discendenti di System.Attribute. Un esempio? L'attributo System.Data.SqlClient.SqlClientPermissionAttribute (e i suoi fratellini).
Per tantissimi sarà ovvia ma la posto brevemente, solo come osservazione: implementare un'interfaccia non conforme al CLS con una classe conforme al CLS è possibile implementando esplicitamente i membri non conformi.
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.”)]
Mi sono ricordato di questa domanda di Giancarlo:
"Perchè nella definizione di una interfaccia non c'è la possibilità di specificare anche una firma per eventuali costruttori?"
incontrando nel framework (Beta 2) l'attributo System.Web.UI.ConstructorNeedsTagAttribute. Praticamente, decorando con [ConstructorNeedsTag(true)] una classe, "garantiamo" al client della classe il fatto che essa abbia un costruttore con un parametro di tipo string.
E' proprio vero il fatto che l'AOP permette di estendere un linguaggio con nuove costruzioni...
Sperimentando in questi giorni delle cose, mi sono fermato su una prova che mi è apparsa piuttosto strana: il seguente snippet
using System; class Foo{ static int i = 0; ~Foo() { Foo f = new Foo(); Console.WriteLine(++i); } static void Main() { Foo foo = new Foo(); }}
stampa a console numeri consecutivi e si ferma senza errore dopo qualche decina di migliaia!...
La spiegazione l'ho trovata solo adesso, dopo tante fantasie sognate, in una pagina (467) di Richter:
"Quando è in corso il regolare arresto di un processo," [...] "ciascun metodo Finalize ha circa 2 secondi di tempo per essere restituito....
Inserendo del codice solo nei posti marcati dai due commenti, il seguente snippet:
using System; class Foo{ // static void Main() { Console.WriteLine("Begin"); // Console.WriteLine("End"); }}
deve stampare a console:
Before beginBeginEndAfter end
Se vi pare troppo semplice, provate a trovare 2 soluzioni differenti (esistono!).
Leggendo questo articolo apparso oggi:
G. Pollice, "Great art and the craft of software development", The Rational Edge (July 2005)
mi sono ricordato di questo post di Andrea Boschin: "Una via italiana al software?". Sembra che davanti a opere di Michelangelo, da Vinci, Tiziano, Caravaggio, Stradivarius, Amati, qualcuno ha pensato a Donald Knuth, Grady Booch, Ken Thompson, Dennis Ritchie, Ivar Jacobson, David Parnas, Kent Beck, Martin Fowler, James Rumbaugh, Niklaus Wirth, Robert Martin.
Ed ecco come, alla domanda di Andrea "perchè i principi che hanno dato vita a delle così efficaci ed apprezzate soluzioni non vengono applicati anche in campo informatico?", il Prof....
Questo codice, assolutamente simmetrico, l'ho scritto apposta per essere compilato sia in C# che in J#, senza modifiche:
// foo.txtclass Test{ static int i = 0; // J# entry point // vjc foo.txt public static void main(System.String[] args) { i++; System.Console.WriteLine(i); Main(args); } // C# entry point // csc foo.txt public static void Main(System.String[] args) { i++; System.Console.WriteLine(i); main(args); }}
Eseguendo il foo.exe, arriva la sorpresa: il numero di cicli che la variante J# riesce a fare (159830), supera il numero di cicli della variante C# (159781), fino al messaggio:
Process is terminated due to StackOverflowException.
Incuriosito da questa strana vittoria del...
Il seguente snippet, quante volte stampa Ciao a console, e perché?
using System; class Foo{ public Foo() { Console.WriteLine(this); } public static Foo MyField = new Ciao();} class Ciao: Foo{ public static Foo MyProperty { get { return new Ciao(); } }} class Test{ static void Main() { Console.WriteLine(Ciao.MyProperty); }}
A. 1
B. 2
C. 3
D. StackOverflowException
Troppo semplice, basta un po' di attenzione...
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...
Come utilizzare un'enumeration come attribute?
Nello snippet seguente mostro un semplice ma elegante pattern per il wrapping di un'enumeration (ho inserito le spiegazioni come commenti nel codice).
Il fatto che il wrapper deriva da System.Attribute è solo per offrire una situazione concreta in cui il wrapping diventa utile e necessario. Con il valore 0 per il primo elemento dell'enumeration ho voluto ricordare una best practice spesso dimenticata: "ensure that 0 is a valid state for value types" (vedi l'item 8 del libro "Effective C#" di Bill Wagner, oppure (aggiornamento 1: 06/07/05) la regola 10.9 nel libro di Balena e Dimauro).
using...