lunedì 28 febbraio 2011

Passaggio di parametri ai metodi in Java e C#


Diamo oggi un’occhiata ad un argomento che ho notato solleva dubbi ai programmatori principianti: come vengono trattati gli oggetti e le variabili passati come argomenti ai metodi, per riferimento o per valore e quali sono le differenze tra i due linguaggi C# e Java.
Iniziamo col dire cosa significa passaggio “per valore” e passaggio “per riferimento” (indipendentemente dal linguaggio utilizzato):
  • Valore: viene creata una copia di una variabile al momento della chiamata, per cui il metodo non agisce direttamente sulla variabile originale ma solo sulla sua copia, quindi all’uscita di tale metodo la variabile originale rimarrà immutata
  • Riferimento: viene passato un riferimento (indirizzo) alla variabile per cui il metodo chiamato agirà direttamente sulla variabile originale tramite il suo riferimento.
Anche le variabili si suddividono principalmente in due tipologie:

  • ValueTypes:  sono i cosiddetti tipi primitivi. Vengono usualmente allocati nello stack. Esempi di tipi primitivi sono int, double, float e le strutture (p. es. la struttura Int32 per .NET)
  • ReferenceTypes: sono i cosiddetti tipi complessi.  Sono tutti gli oggetti definiti tramite una “class”. Usualmente tali tipi vengono allocati nel Managed Heap tramite una new. Esempi sono le classi che noi creiamo, oppure quelle preesistenti nel framework (p.es. la classe Integer in Java è un Reference Type)


In Java e C# tutte le variabili (ValueTypes o ReferenceTypes) vengono passate ai metodi, di default, per valore. Sfatiamo il mito che dice che in Java le variabili primitive sono passate per valore e gli oggetti per riferimento: è corretto invece dire che in Java (come in C#), vengono passati i riferimenti agli oggetti per valore,  così come confermato dallo stesso James Gosling, uno degli inventori di Java:

“Some people will say incorrectly that objects are passed “by reference.” In programming language design, the term pass by reference properly means that when an argument is passed to a function, the invoked function gets a reference to the original value, not a copy of its value. If the function modifies its parameter, the value in the calling code will be changed because the argument and parameter use the same slot in memory…. The Java programming language does not pass objects by reference; it passes object references by value. Because two copies of the same reference refer to the same actual object, changes made through one reference variable are visible through the other. There is exactly one parameter passing mode — pass by value — and that helps keep things simple.”
James Gosling – The Java Programming Language, 4th Edition

Java e C# hanno quindi lo stesso comportamento. Vediamo un esempio in Java sui tipi primitivi:

public class A{
  ...
   void A_method(int k){
     int i=10*k; //questa i è locale al metodo e non ha niente a che fare con quella esterna
     k=5; //qui si modifica il valore di una variabile locale al metodo (k)
     ...
   }
 }

...  
int i=2;
A a;
a.A_method(i);

int j=i;//qui si avrà j=i=2 e non 5!
...

La chiamata al metodo passando per valore la variabile i, non sortisce su questa alcun effetto anche se A_method copia il valore di i su un’altra variabile k. In C# il discorso è analogo.
Il discorso per i ReferenceTypes è invece differente e merita un pò di attenzione.
Soprattutto in C#, il passaggio per valore di un oggetto (che è quello di default), può creare qualche confusione iniziale e qualche sorpresa.  Abbiamo visto che passare un oggetto per valore significa che verrà creata una copia non dell’oggetto stesso, ma del suo riferimento. Ciò significa che se io chiamo un metodo e gli passo un oggetto, verrà creata una copia del riferimento e cioè tale copia del riferimento punterà all’oggetto stesso: il risultato è che le modifiche apportate ad un oggetto dentro al metodo in C# saranno persistenti.
Si considerino le seguenti classi C#:
  class A
  {
      public int val {get;set;}
  }

  class Class1
  { 
     public void A_byVal1(A k)
     {
       k.val = 10; //qui i riferimenti di k e ag sono gli stessi e l’istruzione agisce su ag
       A b = new A(); //questo new crea un nuovo riferimento
       b.val = 100;
       k = b; //questo copia il riferimento di b in k
       k.val = 200; //qui k.val e b.val valgono entrambi 200, ma ag.val vale sempre 10!
     }
  }

con le seguenti istruzioni:

Class1 c1 = new Class1();
A ag1 = new A();
ag1.val = 1;
c1.A_byVal1(ag1); //all’uscita ag1.val vale 10: non 1, non 100 e nemmeno 200!

All’uscita del metodo A_byVal1, ag1.val  vale 10 perchè al momento della chiamata di A_byVal1 viene creata una copia della reference di ag1 (ref_ag1), chiamiamola ag1_ref_copy: ma allora ref_ag1 e ag1_ref_copy puntano entrambe ad ag1 e da ciò si capisce come k.val = 10; vada a modificare il reale valore dell’oggetto ag1 originale. La new dentro il metodo crea un altro oggetto b nel Managed Heap, ne inizializza il valore a 100 e copia il riferimento dell’oggetto b in k: ciò significa che l’oggetto originale ag non viene più modificato poichè il riferimento di k punta ad una variabile locale b, che verrà segnata per l’eliminazione all’uscita del metodo dal GC.
Il meccanismo per passare variabili (primitive e non) ad un metodo per riferimento in C# prevede l’utilizzo della parola chiave ref (tale metodologia ha origini probabilmente dal C++ in cui si usava il carattere & per indicare il passaggio per riferimento di una variabile):

public void A_byRef2(ref A k)
{
    //qui k e ag puntano alla stessa locazione (ag.val = 0)
    A b = new A(); //questo new crea un nuovo riferimento
    b.val = 10;
    k = b; //questo copia il riferimento di b in k, ma quindi anche ag punta a b
}

Class1 c1 = new Class1();
A ag1 = new A();
ag1.val = 0;
c1.A_byRef2(ref ag); //all’uscita ag1.val vale 10, non 0!

Come si nota, pur creando un riferimento locale, all’uscita del metodo  ag.val  varrà 10: ciò avviene perchè al momento della chiamata non viene fatta una copia del riferimento di ag, ma viene usata proprio quella locazione: per cui quando copio b in k, sto copiando proprio sulla locazione di memoria puntata da ag. A differenza del caso del passaggio per valore (metodo A_byVal1), all’uscita del metodo la locazione creata nello heap con la new (b), non verrà segnata per la distruzione dal GC perchè puntata ancora da ag. Qualcuno potrebbe chiedersi come mai l’operatore di assegnamento delle variabili ReferenceType agisce sui riferimenti e non sui valori: il motivo è che quando usiamo gli operatori noi passiamo le variabili per valore e se usiamo i ReferenceTypes passeremo per valore i riferimenti alle variabili contenenti i valori. In .NET e Java i vari operatori (=,+=,-= ecc.), come tutti i metodi che ricevono ReferenceTypes, agiscono sull'indirizzo di memoria dell'oggetto, ovvero l'indirizzo che fa riferimento all'oggetto. Come noto invece, i ValueTypes contengono invece direttamente il valore e non sono sotto il controllo del GC. La differenza tra ValueTypes e ReferenceTypes spesso viene mascherata dal meccanismo di boxingunboxing (wrapping – unwrapping in Java) che consiste nell’incapsulare il ValueType dentro un ReferenceType. Proviamo a sottoporre alle stesse operazioni due variabili, una ValueType e una ReferenceType.
Data la classe A vista prima, si consideri il seguente esempio in C#:

A d_ref1 = new A();  // Allocato nell'heap
double d_val1;      // Allocato nello stack
d_ref1.val = 10;    // Cambia il riferimento a cui punta
d_val1 = 10;        // Cambiato il valore nello stack
A d_ref2 = d_ref1;     // Copia il solo riferimento (puntatore)
double d_val2 = d_val1;         // Alloca nello stack e copia il valore
d_ref1.val = 20;
d_val1 = 20;
Console.WriteLine("d_ref1.val = {0}, d_ref2 = {1}", d_ref1.val, d_ref2.val);
Console.WriteLine("d_val1 = {0}, d_val2 = {1}", d_val1, d_val2);

Eseguendo questo codice si otterrà il seguente output:

d_ref1.val = 20, d_ref2.val = 20
d_val1 = 20, d_val2 = 10

Ossia quando si applica l’operatore di assegnamento (cfr. metodo) ai ValueTypes e ai ReferenceTypes si ha un comportamento differente perchè l’operatore di assegnamento agisce direttamente sui valori dei primi e sugli indirizzi dei secondi (d_val1 = 20 non interferisce con d_val2, mentre d_ref1.val = 20; fa in modo che pure d_ref2.val punti alla locazione contenente 20. Nota: in questo esempio C# non potevamo usare Double al posto di A perchè essa, pur effettuando il boxing di double, è una structure che comunque viene allocata nello stack di default.

Anche in Java gli oggetti Reference sono passati ai metodi per valore dei riferimenti, perciò l'oggetto referenziato può essere modificato dal metodo. Il comportamento è del tutto simile al C#. Per verificarlo facciamo un esempio.
Supponiamo di avere le seguenti  classi Java:

public class A {
  int val=0;
   ...
}
public class Class1
{
   void A_byRef(A k) {
     k.val=20; //qui i riferimenti di k e ag sono gli stessi e l’istruzione agisce su ag
     ...
     k= new A(); //qui viene creato un nuovo riferimento per k
     k.val=100; //questa istruzione agisce solo su k locale
   }
}

Effettuiamo una chiamata al metodo A_byRef:

  Class1 c1 = new Class1();
  A ag = new A();
  ag.val = 5;  
  c1.A_byRef(ag);

  int i = ag.val; //i vale 20 non 5 e nemmeno 100

Il caso di Java è del tutto simile a quello di C#: il riferimento di ag viene passato per valore cosicchè alla chiamata viene fatta una copia di ref_ag, ref_ag_copy. A questo punto l’istruzione (e solo quella) k.val=20; agirà sull’istanza di ag originale, mentre la copia creata successivamente con la new crea un nuovo riferimento ad un’altra istanza di oggetto nello heap di tipo A, per cui k.val=100 non ha ripercussioni su ag.
A questo punto nasce una domanda spontanea: si possono passare variabili in Java per riferimento come in C#? La risposta è no! Non esiste in Java l’equivalente della parola chiave ref e ciò spiega come mai non potremmo mai scambiare due variabili int dentro un metodo Java senza l’utilizzo della tecnica del boxing!

public void badSwapInJava(int var1, int var2)
{
  int temp = var1;
  var1 = var2;
  var2 = temp;
}

Il metodo badSwapInJava() non altera le variabili passate. Facendo il boxing (alias wrapping) otteniamo il risultato voluto:

public void boxingSwapInJava(MyInteger rWrap, MyInteger sWrap) {
      int t = rWrap.getValue();
      rWrap.setValue(sWrap.getValue());
      sWrap.setValue(t);
}

giovedì 24 febbraio 2011

Interfacce grafiche in .NET con il linguaggio XAML



Quando si vogliono realizzare delle applicazioni in .NET con Silverlight, oppure delle presentazioni WPF (Windows Presentation Forms) si ha bisogno di conoscere il linguaggio XAML (eXtensible Application Markup Language). XAML è un linguaggio sviluppato all'interno del .NET Framework, che ci permette di descrivere delle interfacce utente per mezzo di un documento XML in modo schematico.
Quali sono le proprietà e perchè è stato scelto di usare XAML?

  • Come per gli altri linguaggi .NET, XAML viene compilato in codice intermedio (IL).
  • Utilizza grafica vettoriale.
  • Le proprietà di presentazione sono parte del linguaggio (a differenza di XUL che utilizza i CSS). Ciò lo rende più facile da scrivere, ma più difficile da modificare.
  • XAML utilizza le librerie e le classi .NET come un vero e proprio linguaggio del Framework
  • XAML dà possibilità di includere codice C# associato (XUL usa Javascript)
  • Gli eventi sono proprietà dei tag e i tag sono widget.

Le regole di XAML sono semplici:
  • Ciascun elemento di un documento XAML viene mappato ad una classe in .NET (es. Button)
  • Gli elementi hanno una struttura gerarchica in modo tale che gli elementi contengono o sono contenuti da altri elementi (es. Button dentro una Window)
  • Si può accedere alle proprietà “semplici” di una classe per mezzo degli attributi di un elemento (es. Button Content”), oppure, se la proprietà è a sua volta un oggetto, tramite un mappaggio proprietà-elemento (es. Button.Background)

Supponiamo di voler creare un bottone contenuto in una finestra che abbia un certo titolo. In C# scriveremo:

namespace PED.Demo
{
    class Program
    {
        [STAThread]
        static void Main()
        {
            var b = new Button
            {
                Content = "Click Me!"
            };
            var w = new Window
            {
                Title = "XAML Demo",
                Content = b
            };
            var app = new Application();
            app.Run(w);
        }
    }
}


Tale finestra può essere espressa anche in linguaggio XAML:

File MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="XAML Demo" Height="350" Width="525">
       <Grid>
             <Button Content="Click Me!" />   
</Grid>
</Window>

Si noti come sia necessario includere i namespaces di XAML.
La finestra dovrà essere poi inclusa all’interno di un’applicazione:

File App.xaml:
<Application x:Class="WpfApplication1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        
    </Application.Resources>
</Application>

Questo semplice esempio ci fa capire come le classi in .NET possano essere mappate in XAML e viceversa. E’ possibile anche usare classi custom all’interno di XAML.  Per esempio:

<Window x:Class="WpfApplication1.MainWindow"
xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
Title=XAML Demo Height=350 Width=525>
  <StackPanel>
    <Button Content=Click Me! />
    <ListBox>
      <local:Person FirstName=Mario LastName=Rossi />
      <local:Person FirstName=Gino LastName=Bianchi />
    </ListBox>
  </StackPanel>
</Window>


In questo esempio abbiamo inizializzato una classe Person definita localmente e che possiede due proprietà “FirstName” e “LastName”. Implementando il metodo ToString() della classe Person potremo riempire la ListBox con nomi e cognomi. In poche righe di codice C# la classe Person è così definita:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public override string ToString()
    {
        return string.Format("{0} {1}", FirstName, LastName);
    }
}


Per utilizzare XAML dobbiamo creare un progetto in Visual Studio che utilizzi tale linguaggio (per esempio un'applicazione WPF o Silverlight). Una volta creato un nuovo progetto in Visual Studio, tale IDE ci creerà automaticamente lo scheletro dell'applicazione con i file App.xaml  e MainWindow.xaml visti in precedenza, nonchè i file C# (.cs) ad essi collegati (code behind). Realizzare una GUI con XAML/WPF in Visual Studio è del tutto simile a crearne una in Windows Forms o ASP.NET, dato che vi è il "solito" sistema WYSIWYG col drag & drop, affiancato però dal codice XAML.
Si voglia ora per esempio creare un bottone colorato con un certo gradiente: dalla toolbox di VS poniamo un bottone nella MainWindow, analizzando il codice XAML possiamo accedere alle proprietà del bottone come se fossero elementi del documento (VS ci permette di accedere a tali proprietà anche in una finestra separata):

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button Content="Click Me!" Height="23"
                HorizontalAlignment="Left"
                Margin="129,36,0,0"
                Name="button1"
                VerticalAlignment="Top"
                Width="75" Click="button1_Click">
            <Button.Background>
                <LinearGradientBrush StartPoint="0.5,0.0" EndPoint="0.5, 1.0">
                    <LinearGradientBrush.GradientStops>
                        <GradientStopCollection>
                            <GradientStop Offset="0" Color="Yellow"/>
                            <GradientStop Offset="0.3" Color="Orange"/>
                            <GradientStop Offset="0.7" Color="Red"/>
                            <GradientStop Offset="1" Color="DarkRed"/>
                        </GradientStopCollection>
                    </LinearGradientBrush.GradientStops>
                </LinearGradientBrush>
            </Button.Background>
        </Button>
    </Grid>
</Window>


Si noti come si definiscono le proprietà del bottone dentro dei tag annidati i cui nomi sono quelli definiti all’interno della classe Button.  Il risultato è un bottone del genere:


Come visto, in XAML gli oggetti possono contenere altri oggetti per cui potremmo pensare di dichiarare allo stesso modo array o collezioni di oggetti:

<x:Array Type="local:Person">
  <local:Person FirstName="Mario" LastName="Rossi" />
  <local:Person FirstName="Gino" LastName="Bianchi" />
</x:Array>

Definisce un array di oggetti Person (x:array è un'estensione di Markup per il supporto alle matrici).
Supponiamo ora di voler collegare l’evento “click” del bottone a del codice C# che visualizzi un messaggio a video.  Cliccando due volte sul bottone dell'anteprima in Visual Studio, verrà creato l'evento button1_Click nel Code Behind associato:

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("Click!");
        }
    }

In questo semplice esempio abbiamo visto un modo per utilizzare XAML. In realtà XAML ha diversi “dialetti”: WPF XAML (XAML per WPF), XPS XAML (XAML per descrivere documenti elettronici), Silverlight XAML (subset di XAML per Silverlight), WF XAML (XAML per Windows Workflow Foundation).  XAML ha dei meccanismi quali:


- Markup Extensions: estensioni utilizzate per settare dinamicamente i valori delle proprietà degli oggetti grafici in funzione di altri oggetti esistenti.                               
Vediamo un esempio con una StaticResourceExtension:

<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="XAML Demo" Height="350" Width="525">
    <StackPanel>
        <StackPanel.Resources>
            <SolidColorBrush
        x:Key="TextBackBrush"
        Color="LightBlue"
        />
        </StackPanel.Resources>
        <TextBlock Background="{StaticResource TextBackBrush}">Hello!</TextBlock>
    </StackPanel>
</Window>

Questo codice disegnerà la scritta "Hello!" su uno sfondo azzurro chiaro: per cambiare il colore dello sfondo basterà modificare la proprietà Color di SolidColorBrush. Ci sono altri modi per creare delle MarkupExtension, ma tralascerò per ora questo argomento.

- Attached Properties: sono delle proprietà che si possono applicare a più controlli differenti, ma la cui definizione è data a livello di classe. Inoltre permettono di creare proprietà “virtuali” che di fatto estendono quelle già presenti in una certa classe. La sintassi da usare è AttachedPropertyProvider.PropertyName.
Per esempio:

<DockPanel>
  <CheckBox DockPanel.Dock="Top">Hello</CheckBox>
</DockPanel>
    DockPanel.Dock è una proprietà valida per tutti gli oggetti DockPanel. Si noti come il concetto è simile alle proprietà statiche delle classi: in C# questo può essere descritto per esempio con:


    DockPanel.SetDock(myCheckBox, Dock.Top);


    Recent Posts

     
    Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | cna certification