Properties με παραμέτρους. Ε;!


Τις προάλλες με ρώτησε ένας μαθητής μου γιατί όταν γράφουμε ένα property, πχ

Public Property MyProperty As Integer

μόλις πατήσουμε το enter παράγται το

    Public Property MyProperty() As Integer
        Get

        End Get
        Set(ByVal value As Integer)

        End Set
    End Property

Που είναι το περίεργο; Οι παρενθέσεις μετά το MyProperty. Τι χρειάζονται;

Λοιπόν, μέσα σε αυτές τις παρενθέσεις μπορούμε να δηλώσουμε μια παράμετρο. Πχ

    Public Property MyProperty(ByVal Param As Integer) As Integer
        Get

        End Get
        Set(ByVal value As Integer)

        End Set
    End Property

Αυτή τη παράμετρο μπορούμε να τη χρησιμοποιήσουμε κανονικά μέσα στον κώδικα του Get/Set, πχ:

MyClass.MyProperty(5) = 3

όπου μέσα στο Set θα έχουμε value=3, Param=5. Ωραίο ε?!

Το θέμα είναι ότι όσο ωραίο κι αν είναι,δεν θα πρέπει να το χρησιμοποιείτε. Είναι ένα feature της VB.NET το οποίο δεν έχει όμως η C# (πράγμα που σημαίνει ότι για να χρησιμοποιήθει η κλάση μέσα από C# θα πρέπει να καταφύγει ο developer στο reflection – λέγεμε και work re-assurance) και ο λόγος που υπάρχει είναι για να διευκολύνεται ο προγραμματισμός με COM

Happy coding!

Advertisements

Migrate από VB6 σε .NET: VB Migration Partner και VB6 Bulk Analyzer


Για όλα αυτά τα legacy applications που έχουν ξεμείνει στην Visual Basic 6, υπάρχουν κάποια νέα βοηθήματα για την περίπτωση που θα αποφασιστεί το migration σε .NET.

O Francesco Balena, guru της VB6 που τα βιβλία του διαβάσαμε και αγαπήσαμε, τα τελευταία χρόνια έχει κάνει σπουδαία δουλειά στο θέμα του migration. Πρόσφατα, κυκλοφόρησε το VB Migration Partner, ένα προϊόν που χειρίζεται άνετα και με εντυπωσιακά ποσοστά επιτυχίας όλα εκείνα τα τμήματα κώδικα που κάνουν Visual Basic Upgrade Wizard να σηκώνει τα χέρια ψηλά.

Παράλληλα, μόλις χθες, έδωσε προς δωρεάν χρήση ένα utility που ονομάζεται VB6 Bulk Analyzer. Πρόκειται για ένα command-line utility το οποίο αναλύει τα VB6 projects που βρίσκονται σε κάποιο directory tree και δημιουργεί ένα report το οποίο δίνει μια σαφή εικόνα για το τι έχει να αντιμετωπίσει ο άμοιρος, εεε ο developer κατά το έργο του migration.

Η αλήθεια είναι ότι οι εφαρμογές που έχουν γραφτεί VΒ6 έχουν αρκετά χμμμ… "μη τυποποιημένα" τμήματα κώδικα, κολπάκια, κλπ έτσι ώστε να υπερκεραστούν οι εγγενείς περιορισμοί της γλώσσας. Ακριβώς αυτά τα τμήματα κώδικα είναι που αποτελούν προκλήσεις για τα εργαλεία μετατροπής κώδικα. Σε όσους λοιπόν το σκέφτονται, τώρα πλεόν είναι πολύ εύκολο να περάσουν τις VB6 εφαρμογές τους σε .NET. Άντε, και καλή τύχη!


Anonymous Types: Προσοχή στις διαφορές VB.NET – C#


Σε παλιότερο post μου, είχα αναφερθεί στα Anonymous Types, τα οποία μαζί με το Type Inference και τους Object Initializers αποτελούν στοιχεία απαραίτητα για το LINQ. Σε αντίθεση με το Type Inference (που είναι απλό ως concept και παρόμοιο στις δύο γλώσσες) και τους Object Initializers (κι αυτό παρόμοιο στις δύο γλώσσες) τα Anonymous Types κρύβουν μια σημαντική διαφορά (λέγε με breaking change) στην υλοποίησή τους! Πιστεύω ότι αυτή η διαφορά είναι απαραίτητο να τη γνωρίζει κανείς, είτε γράφει τακτικά και στις δύο γλώσσες, είτε πετύχει κάποιο code sample και πρέπει να το μετατρέψει από VB.NET σε C# ή το αντίστοφο.

Στη C#, όταν δύο anonymous types δημιουργούνται με properties που έχουν ίδιο όνομα, ίδια σειρά και ίδιο τύπο, τότε και τα anonymous types έχουν ίδιο τύπο. Αν αντίστοιχα objects έχουν properties με ίδιες τιμές, τότε τα δύο instances έχουν ίδιο τύπο και είναι equal. Δηλαδή στο

var w = new { FirstName = "Manos", LastName = "Kelaiditis" };
var x = new { FirstName = "Manos", LastName = "Kelaiditis" };
var y = new { FirstName = "Kelaiditis", LastName = "Manos" };
var z = new { LastName = "Kelaiditis" , FirstName = "Manos"};
Console.WriteLine(w.Equals(x) ? "Match" : "NoMatch");
Console.WriteLine(w.Equals(y) ? "Match" : "NoMatch");
Console.WriteLine(w.Equals(z) ? "Match" : "NoMatch");

μόνο το w είναι ίδιο με το x. Tο Equals στα anonymous types χρησιμοποιεί το hashcode του instance το οποίο υπολογίζεται προσθέτοντας το hashcode από κάθε member. Με λίγα λόγια, το hashcode εξαρτάται από τη δομή και τις τιμές. Τώρα το θέμα είναι το εξής: Σε συνέπεια του παραπάνω το hashcode χρησιμοποιείται σε πολλές περιπτώσεις, όπως όταν τα anonymous types μπαίνουν σε collections τύπου HashTable, όταν χρησιμοποιούμε grouping και filtering στα Linq queries αλλά ακόμα και στο data binding των collections. Ουσιαστικά τo hashcode παίζει το ρόλο κλειδιού. Αυτό σημαίνει ότι το hashcode δεν επιτρέπεται να αλλάξει ποτέ στη διάρκεια ζωής του object. Γι αυτό και οι σχεδιαστές της C# αποφάσισαν ότι τα Anonymous Types θα είναι Immutable. Όταν διαβάζετε Immutable θα σκεφτόσαστε "χαραγμένα σε πέτρα". Αυτό σημαίνει ότι αν τολμήσω να γράψω το παρακάτω:

z.LastName = "Georgiou";

θα πάρω το λάθος:

Error    1    Property or indexer ‘AnonymousType#1.LastName’ cannot be assigned to — it is read only   

καθώς τα instances από anonymous types είναι read only. Οι σχεδιαστές της C# θεώρησαν ότι τα "read-only" instances από anonymous types δεν φαίνεται να είναι ιδιαίτερο πρόβλημα αφού τα anonymous types έχουν περιορισμένη χρήση καθότι τυπικά δεν μπορούν να χρησιμοποιηθούν εκτός του context που έχουν δημιουργηθεί.

Τώρα, στη ομάδα της VB, είδαν ότι αν υιοθετούσαν το immutable χαρακτηριστικό για τα anonymous types θα ήταν (για λόγους συμβατότητας) μια απόφαση μονόδρομος που θα τους εμπόδιζε να είναι ευέλικτοι αφού αφενός λόγω late binding μπορεί να ανατραπεί το προηγούμενο και αφετέρου μελλοντικά θα προστεθούν χαρακτηριστικά όπως nominal anonymous types και dynamic interfaces.

Έτσι λοιπόν στη VB υπάρχει ο modifier "Key" για τα πεδία από τα anonymous types. Ο Key modifier κάνει το πεδίο read-only και καθοδηγεί τον compiler να κάνει override τις μεθόδους Equals και GetHashCode. Ας δούμε ένα παράδειγμα:

Dim x = New With {.FirstName = "Manos", .LastName = "Kelaiditis"}
Dim y = New With {.FirstName = "Manos", .LastName = "Kelaiditis"}
Dim z = New With {.LastName = "Manos", .FirstName = "Kelaiditis"}
Console.WriteLine(If(x.GetType() Is y.GetType(), "TypeMatch", "NoTypeMatch")) ' TypeMatch
Console.WriteLine(If(x.GetType() Is z.GetType(), "TypeMatch", "NoTypeMatch")) ' NoTypeMatch

Το πρώτο αποτέλεσμα δίνει TypeMatch ενώ το δεύτερο NoTypeMatch καθώς τα member του z είνα σε διαφορετική σειρά σε σχέση με το y. Μέχρι εδώ καλά. Ας δούμε το παρακάτω:

Dim x1 = New With {.FirstName = "Manos", .LastName = "Kelaiditis"}
Dim y1 = New With {.FirstName = "Manos", .LastName = "Kelaiditis"}
Console.WriteLine(If(x1.Equals(y1), "Match", "NoMatch")) ' NoMatch

Σε αντίθεση με αυτό που ισχύει στη C#, εδώ δεν παίρνουμε "Match" γιατί έχουμε mutable types αφού δεν έχουμε προσδιορίσει το Key. Απόδειξη είναι ότι μπορούμε άνετα να γράψουμε

x1.LastName = "Georgiou" 

Άρα, για να έχουμε αντιστοιχία με τη C# θα πρέπει να γράψουμε

Dim x2 = New With {Key .FirstName = "Manos", Key .LastName = "Kelaiditis"}
Dim y2 = New With {Key .FirstName = "Manos", Key .LastName = "Kelaiditis"}
Console.WriteLine(If(x2.GetType Is y2.GetType, "TypeMatch", "No TypeMatch"))
Console.WriteLine(If(x2.Equals(y2), "Match", "NoMatch"))

Βέβαια, anonymous types χρησιμοποιούμε και στα Linq queries. Τα παρακάτω είναι ισοδύναμα καθώς παράγονται immutable objects και στις δύο περιπτώσεις:

var query1 = from customer in customers
             select new { customer.FirstName, customer.LastName };
Dim query2 = From customer In customers _ 
             Select customer.FirstName, customer.LastName

Εκεί που υπάρχει διαφορά και χρειάζεται προσοχή είναι όταν χρησιμοποιούμε το New στη VB:

Dim query3 = From customer In customers _
             Select New With {.FirstName = customer.FirstName, _
                              .LastName = customer.LastName}

To παραπάνω query δεν είναι ισοδύναμο με το query2, παράγει mutable objects! Για να γράψουμε ισοδύναμο query με το query1 χρησιμοποιώντας το New, θα πρέπει να γράψουμε:

Dim query3 = From customer In customers _ 
             Select New With {Key .FirstName = customer.FirstName, _
 	                     Key .LastName = customer.LastName}

Τωρά, εκεί που ενδέχεται να κολλήσετε αν μεταφράζετε κώδικα από VB σε C# είναι αν σας τύχει το παρακάτω query:

Dim query = From prod In Products _
            Select New With {Key prod.Name, _
                             Key prod.CostPrice, _
                             prod.SalesPrice}

Παρατηρήστε ότι μόνο το Name και το CostPrice έχουν Key modifier ενώ το SalesPrice δεν έχει. Αυτό δεν μπορείτε να το περάσετε σε C# και θα πρέπει να καταφύγετε στη χρήση κανονικών κλάσεων. Βέβαια, το παραπάνω δίνει αρκετή ευελιξία καθώς για παράδειγμα το αποτέλεσμα του query μπορεί να γίνει data bound σε ένα grid και να επιτρέπονται οι αλλαγές στο πεδίο SalesPrice.


Late Binding: Revisited (και γιατί πρέπει να μας νοιάζει)


Σε συνέχεια του post περί late binding, ας δούμε πως μπορούμε να πετύχουμε σωστό late binding, χωρίς να καταφύγουμε στο quick-and-dirty Option Strict Off.

H μαγική λέξη είναι "Reflection". Τι είναι το Reflection μέσα σε 10»: Είναι ένας μηχανισμός που επιτρέπει την αναζήτηση πληροφοριών σχετικές με μια κλάση κατά το runtime. Δηλαδή, μπορούμε κατά το runtime να βρούμε τι members έχει μια κλάση, τι παραμέτρους δέχονται και τι τιμές επιστρέφουν. Αυτό είναι εξαιρετικά χρήσιμο! Είναι ο μηχανισμός πάνω στον οποίο βασίζεται τo Intellisence και τώρα θα τον χρησιμοποιήσουμε για late binding.

Ας ορίσουμε μια κλάση:

Class TestClass
    Public Function DoSomething(ByVal value As Integer) As Integer
        Return value * 2
    End Function
End Class

Και τώρα ας δούμε πώς θα τη χρησιμοποιήσουμε:

        Dim objTest As New TestClass
        Dim myType As Type
        Dim param(0) As Object
        Dim rslt As Integer

        Dim parameter As String = 5
        Dim functionName As String = "DoSomething"

        param(0) = CType(parameter, Integer)                myType = objTest.GetType 

        rslt = CType(myType.InvokeMember(functionName, _
                     Reflection.BindingFlags.Default Or _
                     Reflection.BindingFlags.InvokeMethod, _
                     Nothing, objTest, param), Integer)

        Console.WriteLine(rslt)

 

Στην πρώτη ομάδα Dim ορίζουμε το object που θα χρησιμοποιήσουμε, και το array από παραμέτρους που θα περάσουμε ως όρισμα στη μέθοδο που θα καλέσουμε. Στη δεύτερη ομάδα Dim ορίζουμε το όνομα της μεθόδου και τις τιμές των παραμέτρων. Κατόπιν με την GetType διαβάζουμε πληροφορίες της κλάσης που ορίσαμε από το manifest. Τι είναι το manifest σε 20»: Πρόκειται για ένα σύνολο από πληροφορίες που εμπεριέχονται σε κάθε assembly (assembly ονομάζεται στο .NET το παραγόμενο αποτέλεσμα του compilation – το dll ή το exe) που αφορά στην περιγραφή του ίδιου του assembly, πχ όνομα του assembly, έκδοση, culture και – αυτό που μας ενδιαφέρει -type reference πληροφορίες).

Οπότε ερχόμαστε και στο ζουμί:

Πάνω στο myType μπορούμε να καλέσουμε την InvokeMember η οποία (όπως και λέει το όνομά της) θα κάνει την κλήση βάσει των ορισμάτων: Το πρώτο είναι το όνομα της μεθόδου. Το δεύτερο, τα BindingFlags, καθοδηγούν το CLR στο πώς θα κάνει το binding. To τρίτο ορίζει το binder. Με το Nothing αφήνουμε το default binder (με custom binders έχουμε επιπρόσθετο έλεγχο κατά την επιλογή της μεθόδου κατά το Invoke). Με την τέταρτη παράμετρο ορίζουμε το πραγματικό object που θα χρησιμοποιηθεί και με την τελευταία περνάμε τις παραμέτρους της μεθόδου. Γενικά, ο κώδικας παραπάνω είναι απλουστευμένος με την έννοια ότι ξέρουμε ήδη ότι η μέθοδος μας επιστρέφει έναν ακέραιο. Στην πραγματικότητα, θα μπορούσαμε να πάρουμε κι αυτή τη πληροφορία μέσω Reflection.

Φυσικά, όλα τα παραπάνω παίζουν και στη C# δηλαδή η διαφορά που έχει η VB με τη C# στο late binding είναι ο πρώτος τρόπος που είδαμε στο προηγούμενο post. Αυτός ο τρόπος είναι by file late binding καθώς καθορίζεται με το Option Strict setting που είναι per file. Από την άλλη μεριά έχει το πλεονέκτημα ότι καθιστά την VB.NET ένα ιδανικό εργαλείο για εφαρμογές office automation και γενικότερα COM interop καθώς απλουστεύει κατά πολύ το implementation (αρκεί να ξέρει κανείς τι κάνει, αυτό που λέμε Static Typing Where Possible, Dynamic Typing When Needed)

To late binding παρότι ως θέμα είναι παλιό, αποτελεί ένα βασικό μηχανισμό ώστε η VB και η C# να αποκτήσουν dynamic χαρακτηριστικά και γι αυτό τώρα αποκτά νέα δυναμική (pun intended). Στην τωρινή μορφή με χρήση Reflection δεν είναι και τόσο εύκολη διαδικασία και γι αυτό ήδη το team της C# σκέφτεται το πως θα ενσωματώσει το late binding στη γλώσσα. Όπως και να έχει, ένα πράγμα είναι σίγουρο: οι εξελίξεις στις γλώσσες του .NET Framework θα είναι συνεχείς και πολύ ενδιαφέρουσες. Αν μπορούσαμε να στρβλώσουμε και τον χρόνο για να βγάλω 30 ώρες στο 24ωρο, θα ήταν ακόμα καλύτερα.


Late Binding και Option Strict


Με αφορμή αυτό το post σκέφτηκα να γράψω δύο πράγματα σχετικά με το late binding και το Option Strict, αυτά που συνήθως αγνοούν οι νέοι προγραμματιστές VB.NET.

Το late binding προέρχεται από τον κόσμο του COM, είναι κάτι που υπήρχε και στη VB, ωστόσο ας μην μπλέξουμε το COM και ας το δούμε λίγο πιο απλά το πράγμα. Στον παρακάτω κώδικα φαίνεται ένα παράδειγμα late binding.

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        AddHandler Button1.Click, AddressOf Button1_Click
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
        Debug.WriteLine(sender.text)
    End Sub

Στο Button1_Click χρησιμοποιούμε το text property στο sender object και με μια πρώτη ματιά φαίνεται να τρέχει σωστά. Σε υποψίες ότι κάτι δεν πάει καλά με αυτή τη γραμμή κώδικα θα πρέπει να μας βάλει το intellisense το οποίο μόλις γράψουμε "sender" και πατήσουμε "." δεν μας εμφανίζει στη λίστα το text property. Ουσιαστικά, εκείνη τη στιγμή λέμε στον compiler "Μην ανησυχείς που αυτό το sender πράγμα δεν έχει text property, όταν τρέξει το πρόγραμμα σου εγγυώμαι ότι ο τύπος του object θα έχει". Όπως αντιλαμβάνεστε, κάτι τέτοιο είναι αρκετά επικίνδυνο γιατί αν κατά το runtime το object δεν έχει τελικά text property θα πάρω ένα ωραιότατο System.MissingMemberException.

Στην C# δεν υπάρχει περίπτωση να γίνει κάτι τέτοιο, θα χτυπήσει η γραμμή κατά το compilation. Στη VB.NET υπάρχει το Option Strict On|Off που καθορίζει αν θα επιτρέπεται η συμπεριφορά που περιέγραψα (Option Strict Off) ή αν θα χτυπάει κατά το compilation (Option Strict On). Και έτσι ερχόμαστε σε έναν πολύ σημαντικό κανόνα. Αν ως προγραμματιστές θα πρέπει να ακολουθείτε έναν και μόνο κανόνα, αυτός είναι ο παρακάτω:

Πάντοτε προγραμματίζουμε με Option Strict On

Μπορούμε είτε να πάμε στα project properties, στα Compile Options και να ενεργοποιήσουμε το On για να ισχύει για όλο το project, είτε να το γράφουμε στη αρχή κάθε vb αρχείου. Προσωπικά προτιμώ το δεύτερο ώστε να το βλέπω, να το θυμάμαι και να είμαι σίγουρος ότι είναι On.

To Option Strict On αποτελεί πολύ καλή προγραμματιστική τακτική καθώς προστατεύει από ύπουλα bugs αφού επιβάλει πάντοτε να γίνεται explicit conversion. Για παράδειγμα, απαγορεύει το παρακάτω:

    Dim b As Double 
    Dim c As Integer

    c = b

Χμμμ…. Ωραία, γιατί όμως υπάρχει το Option Strict Off; To Option Strict Off οφείλεται στην "κληρονομιά" της Visual Basic 6. Εκεί, διευκόλυνε τον προγραμματισμό σε COM objects, ιδιαίτερα με εφαρμογές που χρησιμοποιούσαν το automation του MS Office. Έτσι λοιπόν χρειάστηκε και στη VB.NET λόγω του upgrade που θα έπρεπε να γίνει σε όλες αυτές τις εφαρμογές. Σήμερα – μέχρι το VSTO να γίνει mainstream – το Option Strict Off συνεχίζει να χρησιμεύει για εφαρμογές MS Office Automation, ιδιαίτερα με παλιότερες εκδόσεις του MS Office. Πέρα από αυτό όμως θα πρέπει μην χρησιμοποιείται καθώς μπορεί να οδηγήσει σε κώδικα που συντηρείται δύσκολα και επιτρέπει τη δημιουργία bugs.

Άρα λοιπόν, αν θέλουμε late binding θα πρέπει υποχρεωτικά να χρησιμοποιήσουμε Option Strict Off; Όχι, σε προσεχές post θα δούμε τον σωστό τρόπο για να γίνεται late binding ακόμα και με το Option On. Μέχρι τότε, happy coding!


Αγάπη, ευτυχία και VB


Αν και όλοι το γνωρίζουμε ότι η VB είναι μια γλώσσα γενικής χρήσης, μέσα στο Χριστουγεννιάτικο πνεύμα, ο Chris Anderson, ο Don Box και η Amanda Silver την χρησιμοποιούν για να μας τραγουδίσουν ένα Χριστουγεννιάτικο τραγούδι.

http://channel9.msdn.com/ShowPost.aspx?PostID=367997%20%20

Τώρα που το σκέφτομαι, μάλλον ο Don Box δεν πρέπει να είναι ο ίδιος. Μάλλον έχει αντικατασταθεί από εξωγήινο. Τόσες αναφορές στη VB στο blog του…

Όπως και να έχει, ευχές για καλές γιορτές σε όλους!


To μέλλον της VB.NET


Τον τελευταίο καιρό έχω παρατηρήσει ότι υπάρχει μια διάχυτη αίσθηση ότι η VB.NET αρχίζει να εγκαταλείπεται και πλέον οτιδήποτε "σοβαρό" γίνεται σε C# (μιλώντας πάντοτε για τον managed κόσμο). Εξάλλου, κι εμείς οι VB.NET MVPs "γκρινιάζουμε" για την έλλειψη samples σε VB.NET για διάφορα νέα SDKs και τις edge τεχολογίες που εμφανίζονται. Πρόσφατα, το VB product group μας έδωσε κάποια στοιχεία σχετικά με το momentum της VB.NET:

  • Visual Basic is the #1 .NET language (as reported by Forrester Research)
  • Visual Basic is most downloaded and registered Express Edition
    1. Visual Basic Express is ranked #1
    2. Visual C++ Express is ranked #2, by a difference of 20% from #1
      (όχι δεν είναι τυπογραφικό :))
  • Visual Basic has the most trafficked MSDN language dev center and blog 
  • VB Team blog is in the top 1% in readership of all MS bloggers

Και όλα αυτά πριν εμφανιστούν τα νέα dynamic χαρακτηριστικά…