Προγραμματισμός Windows Κεφάλαιο 3: The.NET Framework Library (FCL) Μέρη έχουν παρθεί από το αγγλικό presentation του καθηγητή Dr. Juan Vargas για το μάθημα CSCE 547 του Department of Computer Science and Engineering University of South Carolina Columbia, SC gr
Υπάρχουν περισσότεροι από 7,000 τύποι (Classes, structs, interfaces, enumerations and delegates) στην FCL. Είναι αδύνατο να επεξηγηθούν όλοι οι τύποι μέσα σε ένα μάθημα ή κεφάλαιο. Το παρόν κεφάλαιο κάνει μια εισαγωγή στην FCL και περιγράφει τους πιο συνηθισμένους τύπους οι οποίοι χρησιμοποιούνται κανονικά ως μέρος της υποδομής πολλών εφαρμογών. Η FCL είναι οργανωμένη σε περίπου 100 namespaces. Οι τύποι που συζητούνται στο παρόν κεφάλαιο είναι οι εξής: I/O (File and Stream) Collections (Hash Tables and Dynamic Arrays) Regular Expressions Internet Classes (HttpWebRequest, HttpWebResponse, System.WebMail) Data (DataReadears, DataSets, Data Adapters) Reflection
Υπάρχουν 30 classes, 1 struct, 3 delegates και 7 enums στο System.IO namespace. Τα πιο κοινώς χρησιμοποιούμενα classes είναι τα BinaryReader, BinaryWriter, BufferedStream, Directory, DirectoryInfo, File, FileInfo, FileStream, FileSystemInfo, IOException, MemoryStream, Path, Stream, StreamWriter, StreamReader, TextReader, TextWriter. Η λίστα αυτή δεν είναι εξαντλητική, αφού IO classes μπορούν να βρεθούν σε άλλα namespaces, π.χ. το NetworkStream είναι μέρος του namespace System.Net.Sockets. Τυπικό I/O: 1. Ανοίγουμε το αρχείο χρησιμοποιώντας ένα FileStream object. 2. Για διάβασμα και γραφή δυαδικών αρχείων, συνδέστε instances των BinaryReader και BinaryWriter με ένα FileStream object και καλέστε μεθόδους των BinaryReader και BinaryWriter όπως Read και Write. 3. Για Ι/Ο κειμένου, συνδέστε ένα StreamReader και StreamWriter με ένα FileStream object και χρησιμοποιείστε μεθόδους των StreamReader και StreamWriter όπως ReadLine και WriteLine. 4. Κλείστε (Close) το FileStream object.
using System; using System.IO; class MyApp { static void Main (string[] args) { // Make sure a file name was entered on the command line if (args.Length == 0) { Console.WriteLine ("Error: Missing file name"); return; } // Open the file and display its contents StreamReader reader = null; try { reader = new StreamReader (args[0]); for (string line = reader.ReadLine (); line != null; line = reader.ReadLine reader.ReadLine ()) Console.WriteLine (line); } catch (IOException e) { Console.WriteLine (e.Message); } finally { if (reader != null) reader.Close (); }
// Use File.Open to create a FileStream, and then wrap a // StreamReader around it FileStream stream = File.Open (filename, FileMode.Open, FileAccess.Read); StreamReader reader = new StreamReader (stream); // Create a FileStream directly, and then wrap a // StreamReader around it FileStream stream = new FileStream (filename, FileMode.Open, FileAccess.Read); StreamReader reader = new StreamReader (stream); // Use File.OpenText to create a FileStream and a // StreamReader in one step StreamReader reader = File.OpenText (filename);
Ο παρακάτω κώδικας θα μπορούσε να χρησιμοποιηθεί για να διαχειριστείτε τα σφάλματα όταν διαβάζετε ένα αρχειο. Όταν παρουσιαστεί ένα σφάλμα, καλείτε την παρακάτω μέθοδο η οποία γράφει το μήνυμα του σφάλματος σε ένα αρχείο. void LogException (string filename, Exception ex) { StreamWriter writer = null; try { writer = new StreamWriter (filename, true); writer.WriteLine (ex.Message); } finally { if (writer != null) writer.Close (); } } Παρατηρήστε την χρησιμοποίηση του try και finally παραπάνω.
Tα BinaryReader και BinaryWriter είναι classes που εφαρμόζουν δυαδικές μεθόδους Διαβάσματος και Γραφής. Το παρακάτω δείγμα διαβάζει και κάνει scramble ένα αρχείο, προσθέτει κάποια κρυπτογράφηση στα δυαδικά περιεχόμενα και γράφει το κωδικογραφημένο περιεχόμενο πίσω στο αρχείο. Η εφαρμογή καλείται με την εντολή: Scramble iFile password O δειγματικός κώδικας περιγράφει το διάβασμα και γράψιμο δυαδικών αρχείων και κάνει μια μικρή εισαγωγή στην κρυπτογράφηση. Παρατηρήστε τα ASCIIEncoding enc = new ASCIIEncoding (); byte[] keybytes = enc.GetBytes (key);
using System; using System.IO; using System.Text; class MyApp { const int bufsize = 1024; static void Main (string[] args) { if (args.Length < 2) { Console.WriteLine (" Syntax: SCRAMBLE filename key"); return; } string filename = args[0]; string key = args[1]; FileStream stream = null; try { stream = File.Open (filename, FileMode.Open, FileAccess.ReadWrite); BinaryReader reader = new BinaryReader (stream); BinaryWriter writer = new BinaryWriter (stream); ASCIIEncoding enc = new ASCIIEncoding (); byte[] keybytes = enc.GetBytes (key); byte[] buffer = new byte[bufsize]; byte[] keybuf = new byte[bufsize + keybytes.Length - 1]; int count = ( keybytes.Length - 1) / keybytes.Length; for (int i=0; i<count; i++) Array.Copy (keybytes, 0, keybuf, i * keybytes.Length, keybytes.Length); long lBytesRemaining = stream.Length; while (lBytesRemaining > 0) { long lPosition = stream.Position; int nBytesRequested = (int) System.Math.Min (bufsize, lBytesRemaining); int nBytesRead = reader.Read (buffer, 0, nBytesRequested); for (int i=0; i<nBytesRead; i++) buffer[i] ^= keybuf[i]; stream.Seek (lPosition, SeekOrigin.Begin); writer.Write (buffer, 0, nBytesRead); lBytesRemaining -= nBytesRead; } Η κρυπτογράφηση γίνεται εδώ
Τα Collections είναι weakly typed – μπορούν να αποθηκεύσουν οποιοδήποτε τύπο αντικειμένου, αρκεί να προέρχεται από το System.Object. Αυτή η λειτουργία όμως κοστίζει πολλά casts των objects. Αυτό το πρόβλημα μπορεί να αποφευχθεί με το να γράψουμε τις δικιές μας collections και καθορίζοντας τη CollectionBase και τη DictionaryBase της στα καινούργια classes. Το πρόβλημα αυτό λύνεται στο.ΝΕΤ 2.0 με την εισαγωγή των Generics. Τα Generics είναι classes, structures, interfaces και methods που παρέχουν σημεία στα οποία βάζουμε έναν ή περισσότερους τύπους που πρόκειται να χρησιμοποιηθούν ή να αποθηκευτούν. Ένα απλό παράδειγμα generics για class είναι το εξής: public class Generic { public T Field; } Όταν φτιάξετε μια instance της συγκεκριμένης class, τότε ορίζετε τον πραγματικό τύπο που θα υποκαταστήσει την παράμετρο T: Generic g = new Generic (); g.Field = "A string"; Στο.ΝΕΤ 2.0 προστέθηκε και η class Collection, με την οποία ορίζετε έναν τύπο αντικειμένων που πρόκειται να αποθηκευτούν στην collection.
Τα Hash tables είναι δομές δεδομένων που αποθηκεύουν ζευγάρια key/values σε θέσεις που λέγονται buckets. Το κυριότερο πλεονέκτημα των hash tables είναι ότι η αναζήτηση είναι πολύ γρήγορη γιατί η «σκληρή» δουλειά γίνεται κατά τη φάση της αποθήκευσης. Στην ουσία, ένα μοναδικό κλειδί αποκτάται για την αποθήκευση των τιμών στα buckets. Ένας συνηθισμένος κώδικας που χρησιμοποιεί HashTables περιλαμβάνει: HashTable aTable = new HashTable( ); aTable.Add( “Sunday”, “Domingo”); // (key,value) pair aTable[“Monday”] = “Lunes”; // table[Key]=value; foreach (DictionaryEntry entry in table) Console.Write(“Key={0}, Value={0}\n”, entry.Key, Entry.Value); aTable.Remove(key); aTable.Clear(); aTable.Count(); και άλλα...
Υπάρχουν δύο τύποι arrays στο.NET: Array είναι μια class που προέρχεται κατεθευθείαν από το System.Object. Αυτή η class μπορεί να χρησιμοποιηθεί για να υλοποιήσετε στατικές (συγκεκριμένου μεγέθους) arrays για την αποθήκευση πρωτόγονων τύπων. Ένα element (στοιχείο) είναι μια τιμή μέσα στην Array. Το length είναι ο συνολικός αριθμός των elements μέσα στο array. Το rank του Array είναι ο αριθμός των διαστάσεων του. Το κάτω όριο μιας διάστασης ενός Array είναι ο πρώτος αριθμοδείκτης εκείνης της διάστασης του Array. Μια πολυδιάστατη Array μπορεί να έχει διαφορετικά όρια για κάθε διάσταση. Τα Type objects παρέχουν πληροφορίες για την δήλωση των array type. Τα Array objects με τον ίδιο Array type μοιράζονται το ίδιο Type object. Το Array έχει 6 ιδιότητες (IsFixedSize, IsReadOnly, IsSynchronized, Length, Rank, SyncRoot), 22 μεθόδους ( BinarySearch, Clear, Copy, Equals, GetValue, GetLength, IndexOf, Initialize, Reverse, SetValue, Sort, ToString, Finalize κ.α.). Η class επίσης εφαρμόζει διάφορες μεθόδους της IList, που περιλαμβάνουν τα ( Add, Clear, Contains, IndexOf, Insert, Remove, RemoveAt ).
H ArrayList είναι μια class που προέρχεται από το System.Collections. Αυτή η class δεν έχει καμία σχέση με τη class System.Object.Array. Η δήλωση της class είναι: [Serializable Serializable] public class ArrayList : IList, ICollection, IEnumerable, ICloneable Το κείμενο δείχνει τις εξής ιδιότητες της class: ArrayList aList = new ArrayList (); aList.Add ("John"); aList.Add ("Paul"); aList.Add ("George"); Το να ξέρουμε πόσα περίπου αντικείμενα θα αποθηκευτούν βοηθάει: ArrayList list = new ArrayList (100000); for (int i=0; i<100000; i++) list.Add (i); Για την ανάκτηση ενός αντικειμένου από την ArrayList, χρησιμοποιήστε μια αριθμοδότηση βασισμένη στο 0: int i = (int) list[0]; To assign a value to an existing array element, do this: list[0] = 999; Η ιδιότητα Count αποκαλύπτει πόσα αντικείμενα περιέχει μια ArrayList. for (int i=0; i<list.Count; i++) Console.WriteLine ( list[i] ); Μπορείτε να ανατρέξετε την ArrayList με το foreach: foreach (int i in list) Console.WriteLine (i); Για να αφαιρέσετε αντικείμενα από την ArrayList, καλέστε το Remove, RemoveAt, RemoveRange, ή Clear.
Το WordCount είναι μια εφαρμογή που δείχνει το I/O, τη χρησιμοποίηση των Array, HashTable, SortedList, και ArrayList. Το πρόγραμμα καλείται με την εξής εντολή: wordcount iFile StreamReader reader = null; Hashtable table = new Hashtable (); Try { reader = new StreamReader (args[0]); for (string line=reader.ReadLine; line!=null;line=reader.ReadLine()) { string[] words = GetWords (line); foreach (string word in words) { string iword = word.ToLower (); if (table.ContainsKey (iword)) table[iword] = (int) table[iword] + 1; else table[iword] = 1; } SortedList list = new SortedList (table); Console.WriteLine ("{0} unique words in {1}", table.Count, args[0]); foreach (DictionaryEntry entry in list) Console.WriteLine ("{0} ({1})",entry.Key, entry.Value); }
static string[] GetWords (string line) { ArrayList al = new ArrayList (); int i = 0; string word; char[] characters = line.ToCharArray (); while ((word = GetNextWord (line, characters, ref i)) != null) al.Add (word); // Return static array equivalent to the ArrayList string[] words = new string[al.Count]; al.CopyTo (words); return words; } static string GetNextWord (string line, char[] characters, ref int i) { // Find the beginning of the next word while (i < characters.Length && ! Char.IsLetterOrDigit (characters[i])) i++; if (i == characters.Length) return null; int start = i; // Find the end of the word while (i < characters.Length && Char.IsLetterOrDigit (characters[i])) i++; // Return the word return line.Substring (start, i - start); } Operate on ArayList and Convert to string[] prior to return
Όταν τρέξετε το πρόγραμμα για τον ίδιο του τον κώδικα.cs, παίρνετε τα εξής: 113 unique words found in class1.c... κ.λ.π.... Καταλαβαίνετε πως πάει. 0 (6) 1 (4) a (5) add (2) again (1) al (4) an (2) and (2) args (4) array (1) arraylist (5) at (1) beginning (1) catch (1) char (4) characters (8) class (1) close (1) collections (1) command (1) console (4) containskey (1) copyto (1) count (3) create (1) creating (1) dictionaryentry (1) display (1) e (2) each (2) else (1) encountered (1) end (1) entered (1) entries (1) entry (4) equivalent (1) error (1) exception (1) file (3) finally (1) find (2) for (3) foreach (2) found (2) from (1) getnextword (2)
Το RegEx είναι μια class προερχόμενη από το name space System.Text.RegularExpressions. Τα RegEx αντικείμενα μπορούν να χρησιμοποιηθούν για: Να χωριστούν strings σε substrings, χρησιμοποιώντας τα RegExps για να αναγνωριστούν τα διαχωριστικά. Να ψάξετε για substrings μέσα στα strings, χρησιμοποιώντας τις RegEx εκφράσεις για να αναζητήσετε σχέδια (patterns). Να κάνετε λειτουργίες Αναζήτησης-Αντικατάστασης χρησιμοποιώντας το RegEx για να αναγνωρίσετε αυτά που πρέπει να αντικατασταθούν στο κείμενο. Ανάλυση αρχείων HTML. Όταν δημιουργούνται RegEx objects, η έκφραση που περιγράφει το pattern χρησιμοποιείται ως ένα όρισμα στο constructor: Using System.Text.RegularExpressions; Regex regex = new Regex ("[a-z]"); Regex regex = new Regex ("[a-z]", RegexOptions.IgnoreCase); Regex regex = new Regex string[] parts = regex.Split foreach (string part in parts) Console.WriteLine (part); c: inetpub wwwroot wintellect Οποιοδήποτε μικρό γράμμα της αλφαβήτας Αν βάλετε ο compiler παραπονιέται Επιστρέφει τα substrings μέσα στο string διαχωρισμένα από το όρισμα μέσα στο RegExp
Για την ανάλυση ενός αρχείου html, με, π.χ., Every good boy does fine Regex regex = new Regex (" ]*>"); string[] parts = regex.Split (" Every good boy does fine "); foreach (string part in parts) Console.WriteLine (part); Αυτός ο κώδικας παράγει: “Every good boy does fine” (μια λέξη ανά γραμμή). Παρατηρήστε ότι “ ]*>” σημαίνει: οτιδήποτε ξεκινά με “ ”, ακολουθόμενο από το “>”. To Regex περιλαμβάνει 3 μεθόδους για αναζήτηση substrings μέσα σε strings: Match, Matches, και IsMatch. Με αυτά, είναι αρκετά εύκολο να γράψει κανείς grep-like. Το πρόγραμμα μπορεί να χρησιμοποιηθεί για να κάνει οποιαδήποτε greps, όπως π.χ. NetGrep index.html “ ]*>” Η παραπάνω εντολή θα εμφάνιζε τις γραμμές μέσα στις οποίες εμφανίζεται το στο αρχείο index.html. Παρόμοια, η εντολή NetGrep file1.txt “\d{2,}” Θα εμφάνιζε τις γραμμές που περιλαμβάνουν ακέραιους αριθμούς με δύο ή περισσότερα ψηφία μέσα στο file1.txt, κ.τ.λ.
using System; using System.IO; using System.Text.RegularExpressions; class MyApp { static void Main (string[] args) { if (args.Length < 2) { Console.WriteLine ("Syntax: NETGREP filename expression"); return; } StreamReader reader = null; int linenum = 1; try { // Initialize a Regex object with the regular expression // entered on the command line Regex regex = Regex (args[1], RegexOptions.IgnoreCase); // Iterate through the file a line at a time and display all lines that contain a pattern matching the regular expression reader = new StreamReader (args[0]); for (string line = reader.ReadLine (); line != null; line = reader.ReadLine (), linenum++) { if (regex.IsMatch (line)) Console.WriteLine ("{0:D5}: {1}", linenum, line); } catch (Exception e) { Console.WriteLine (e.Message); } finally { if (reader != null) reader.Close (); }
Αυτές οι classes υπάρχουν στο name space System.Net. Υπάρχουν 36 classes, 5 interfaces, ένα delegate, και 4 enumerations. Οι πιο σημαντικές classes είναι: Cookie, CookieCollection, Dns, FileWebRequest, FileWebResponse, HttpVersion, HttpWebRequest, HttpWebResponse, IPAddress, IPEndPoint, IPHostEntry, NetworkCredential, ProtocolViolationException, SocketAddress, WebClient, WebException, WebPermission, WebProxy, WebRequest, WebResponse και άλλα. Ο επόμενος κώδικας δείχνει μόνο πως να χρησιμοποιήσετε το WebRequest και WebResponse στο LinkList.cs, που παίρνει ένα URL ως όρισμα. Η εφαρμογή πάει στο URL και εμφανίζει τις γραμμές που περιλαμβάνουν αναφορές σε άλλα websites, π.χ. Υπερσυνδέσμους. Ο κώδικας είναι αρκετά απλός.
using System.IO; using System.Net; using System.Text.RegularExpressions; class MyApp { static void Main (string[] args) { if (args.Length == 0) { Console.WriteLine ("Error: Missing URL"); return; } StreamReader reader = null; try { WebRequest request = WebRequest.Create (args[0]); WebResponse response = request.GetResponse (); reader = new StreamReader (response.GetResponseStream ()); string content = reader.ReadToEnd (); Regex regex = new Regex ("href\\s*=\\s*\"([^\"]*)\"", RegexOptions.IgnoreCase); MatchCollection matches = regex.Matches (content); foreach (Match match in matches) Console.WriteLine (match.Groups[1]); } catch (Exception e) { Console.WriteLine (e.Message); } finally { if (reader != null) reader.Close (); }
Είναι αρκετά εύκολο να στείλετε από ένα.NET Framework πρόγραμμα (αρκεί να έχετε τις σωστές άδειες). Το System.Web.Mail παρέχει ένα managed interface για το SMTP. Οι κυριότερες classes είναι: 1. MailMessage, που αναπαριστά τα μηνύματα s; 2. MailAttachment, που αναπαριστά τα συννημένα αρχεία; 3. SmtpMail, που χειρίζεται την υπηρεσία μηνυμάτων SMTP του συστήματος. Αυτά είναι τα βήματα για να σταλθούν s με το System.Web.Mail: MailMessage message = new MailMessage (); message.From = message.To = message.Subject = “What have I done to deserve this?”; message.Body = “Your music was selected for tonight."; SmtpMail.SmtpServer = "localhost"; SmtpMail.Send (message);
void OnSend (Object sender, EventArgs e) { MailMessage message = new MailMessage(); message.From = Sender.Text; message.To = Receiver.Text; message.Subject = Subject.Text; message.Body = Body.Text; SmtpMail.SmtpServer = "localhost"; SmtpMail.Send (message); } Simple SMTP Client From: To: Subject: Message:
Υπάρχει μια πληθώρα από τεχνολογίες Βάσεων Δεδομένων (ODBC, DAO, RDO, ADO, και OLEDB). Αυτές οι τεχνολογίες συνέπραξαν στην πορεία προς το ADO.NET, το οποίο εφαρμόζεται στο System.Data namespace. Αυτό το κεφάλαιο κάνει μια εισαγωγή στο ADO.NET δείχνωντας πως χρησιμοποιούνται οι πιο συνηθισμένες classes. Το ADO.NET έχει δύο τύπους classes, αυτούς που είναι βελτιστοποιημένοι για τον Microsoft SQL Server και όλους τους άλλους που περιλαμβάνονται στο OleDB, το οποίο δεν είναι γρήγορο αλλά είναι πιο γενικό. Οι classes είναι: Connection (OleDbConnection, SQLConnection), Command (SQLCommand and OleDbCommand), DataReader (OleDbDataReader, SQLDataReader), DataSet, DataAdapter, and Exception.
Όπως είπαμε, τα περισσότερα αντικείμενα του System.Data εφαρμόζονται σε δύο εκδοχές: SQL και OLEDB. Οι Data Readers δεν αποτελούν εξαίρεση. SqlConnection connection = new SqlConnection("server=localhost;uid=sa;pwd=;database=pubs") connection.Open (); SqlCommand command = new SqlCommand ("select * from titles", connection); SqlDataReader reader = command.ExecuteReader(); while (reader.Read()) Console.WriteLine(reader.GetString(1)); connection.Close (); Για απλότητα, ο παραπάνω κώδικας δε διαχειρίζεται τυχόν σφάλματα. Ο επόμενος κώδικας τα διαχειρίζεται.
SqlConnection connection = new SqlConnection ("server=localhost;uid=sa;pwd=;database=pubs"); Try { connection.Open (); SqlCommand command = new SqlCommand ("select * from titles", connection); SqlDataReader reader = command.ExecuteReader (); While (reader.Read ()) Console.WriteLine (reader.GetString (1)); } catch (SqlException e) { Console.WriteLine (e.Message); } finally { connection.Close (); } Παρόμοιος κώδικας μπορεί να χρησιμοποιηθεί για οποιοδήποτε άλλον OleDb data provider, απλά αντικαθιστώντας τους SQL types με τους αντίστοιχους OleDB types.
Όπως το όνομα υπονοεί, οι Data Readers μπορούν μόνο να πάρουν δεδομένα από μια πηγή δεδομένων. Αν χρειάζονται λειτουργίες γραφής, τότε ένα command object χρειάζεται. SqlConnection connection = new SqlConnection ("server=localhost;uid=sa;pwd=;database=pubs"); try { connection.Open (); string sqlcmd = "insert into titles (title_id, title, type, pub_id, price, advance, royalty, ytd_sales, notes, pubdate) values ('BU1001', 'Programming Microsoft.NET', 'Business', '1389', NULL, NULL, NULL, NULL, 'Learn to program Microsoft.NET', 'Jan ')"; SqlCommand command = new SqlCommand (sqlcmd, connection); command.ExecuteNonQuery (); } catch (SqlException e) { Console.WriteLine (e.Message); } finally { connection.Close (); }
Το DataSet είναι μια βάση δεδομένων βασισμένη στη μνήμη και μπορεί να περιέχει πολλαπλούς πίνακες δεδομένων με constraints και relationships. Τα DataSets χρησιμοποιούνται μαζί με τους DataAdapters για τη διευκόλυνση της πρόσβασης διαβάσματος/γραφής στις βάσεις δεδομένων. SqlDataAdapter adapter = new SqlDataAdapter ( "select * from titles", "server=localhost;uid=sa;pwd=;database=pubs« ); DataSet ds = new DataSet (); adapter.Fill (ds); foreach (DataRow row in ds.Tables[0].Rows) Console.WriteLine (row[1]); Παρατηρήστε τη χρήση του foreach, η οποία σημαίνει ότι το DataSet έχει collections από στήλες και γραμμές. Αυτή η λειτουργία διευκολύνει πάρα πολύ τον προγραμματισμό.
Οι Managed εφαρμογές αναπτύσσονται σε assemblies που περιέχουν πολλαπλά αρχεία και μπορούν να περιέχουν πολλαπλά modules με managed ή unmanaged κώδικα. Τα managed modules περιέχουν metadata (~ επιπλέον, συνήθως περιγραφικές, πληροφορίες) που περιγράφουν το περιεχόμενο τους και τις δομές δεδομένων μέσα σε κάτι που καλείται «manifest». Αυτές οι πληροφορίες μπορούν να ανακτηθούν χρησιμοποιώντας το ILDASM, ή μέσω των classes που εφαρμόζονται μέσα στο System.Reflection namespace. Στο.ΝΕΤ, reflection σημαίνει έλεγχος του metadata για την ανάκτηση πληροφοριών για ένα assembly, ένα module ή ένα type. Το VS.Net, π.χ., χρησιμοποιεί το reflection για την ανάκτηση Intellisense Data.
Υπάρχουν τρία σχετικά namespaces: System.Reflection.Assembly System.Reflection.Module System.Type Το πρόγραμμα asminfo.cs που ακολουθεί, δείχνει πως το Reflection χρησιμοποιείται για την ανάκτηση πληροφοριών για ένα αρχείο file.dll. Παρατηρήστε τη χρήση των ακόλουθων τύπων: Assembly, AssemblyName, Version, Module[ ], Type[ ], AssemblyName[ ].
class MyApp { static void Main (string[] args) { if (args.Length == 0) { Console.WriteLine ("Error: Missing file name"); return; } try { Assembly a = Assembly.LoadFrom (args[0]); AssemblyName an = a.GetName (); byte[] bytes = an.GetPublicKeyToken (); if (bytes == null) Console.WriteLine ("Naming: Weak"); else Console.WriteLine ("Naming: Strong"); Version ver = an.Version; Console.WriteLine ("Version: {0}.{1}.{2}.{3}", ver.Major, ver.Minor, ver.Build, ver.Revision); Console.WriteLine ("\nModules"); Module[] modules = a.GetModules (); foreach (Module module in modules) Console.WriteLine (" " + module.Name); Console.WriteLine ("\nExported Types"); Type[] types = a.GetExportedTypes (); foreach (Type type in types) Console.WriteLine (" " + type.Name); Console.WriteLine ("\nReferenced Assemblies"); AssemblyName[] names = a.GetReferencedAssemblies (); foreach (AssemblyName name in names) Console.WriteLine (" " + name.Name); } catch (Exception e) { Console.WriteLine (e.Message); } } Εφαρμοσμένα ως collections
Μπορούμε να φανταστούμε τις attributes ως τρόπους να “διακοσμήσουμε” ή να καθορίσουμε λεπτομέρειες γύρω από μια δήλωση στον κώδικα μας. Για παράδειγμα, τα access modifiers μιας class (public, protected, private) είναι attributes. Οι attributes του SDK μπορούν να βρεθούν χρησιμοποιώντας το namespace System.Reflection.{AssemblyConfigurationName, AssemblyCultureName, …}. Η C# μας δίνει την δυνατότητα να ορίσουμε attributes στο επίπεδο του assembly, module, class, struct, interface, enum, delegate, method, parameter, field, property (indexer, getter, setter), event (field, property, add, remove). Αυτό είναι δυνατόν γιατί υπάρχουν τρεις classes (κυριολεκτικά) των attributes: AttributeUsage, Conditional, and Obsolete.
Οι Attributes είναι δηλώσεις κώδικα που προστίθενται για να προσδιορίσουν ή να ανακτήσουν πληροφορίες για metadata. Αν χρησιμοποιηθούν έξυπνα, οι δηλώσεις attribute μπορούν να αποδειχτούν ένα εξαιρετικό εργαλείο για τον ομαδικό προγραμματισμό και για τη διαχείριση των εκδόσεων (version control). Μια attribute εφαρμόζεται στο κομμάτι κώδικα που βρίσκεται αμέσως μετά από τη δήλωση της: using System.Diagnostics; [Conditional ("DEBUG")] public void DoStuff () {... } Αυτό καλείται «δώσιμο μιας attribute σε μια μέθοδο». Αν ο κώδικας γίνει compiled χωρίς ένα “DEBUG” σύμβολο, ένα token τοποθετείται στο metadata για να υποδηλώσει ότι το DoStuff δε θα κληθεί. Αν κάποιος άλλος κώδικας καλέσει το DoStuff σε release mode, η κλήση θα αγνοηθεί. Το DoStuff θα εκτελεστεί μόνο αν το module του γίνει compiled σε DEBUG mode. Παρατηρήστε το System.Diagnostics;.
[AttributeUsage (AttributeTargets.All, AllowMultiple=true)] class CodeRevisionAttribute : Attribute { public string Author; public string Date; public string Comment; public CodeRevisionAttribute (string Author, string Date) { this.Author = Author; this.Date = Date; } Ο κώδικας μπορεί να χρησιμοποιηθεί ως εξής: [CodeRevision ("billg", " ")] [CodeRevision ("steveb", " ", Comment="Fixed Bill's bugs")] struct Point { public int x; public int y; public int z; } Ο compiler προσθέτει την Attribute
MemberInfo info = typeof (Point); object[] attributes = info.GetCustomAttributes (false); if (attributes.Length > 0) { Console.WriteLine ("Code revisions for Point struct"); foreach (CodeRevisionAttribute attribute in attributes) { Console.WriteLine ("\nAuthor: {0}", attribute.Author); Console.WriteLine ("Date: {0}", attribute.Date); if (attribute.Comment != null) Console.WriteLine ("Comment: {0}", attribute.Comment); } Code revisions for Point struct Author: billg Date: Author: steveb Date: Comment: Fixed Bill's bugs Πληροφορίες από τη Attribute
Το Dynamic Loading (δυναμική φόρτωση) αναφέρεται στη διαδικασία εισαγωγής modules (τυπικά από.dll αρχεία) όταν χρειάζονται. Τα Plug-ins είναι ένα καλό παράδειγμα του dynamic loading. Φανταστείτε τα ως βδέλλες που κολλάνε στον κώδικα σας, ή αν προτιμάτε, ως ενσωματωμένα τσιπ που προσθέτετε στην motherboard ή breadboard σας. Το Dynamic Loading πραγματοποιείται χρησιμοποιώντας μια τεχνική που λέγεται “Late Binding”, που ουσιαστικά γίνεται με την αναβολή της ανάθεσης των τύπων (type assignment) ενός καινούργιου object μέχρι να χρειαστούν πληροφορίες για τους τύπους (για να πάρουμε τα μέλη τους, το μέγεθος τους, κ.τ.λ.). Το Reflection κάνει το Late Binding για το Dynamic Loading δυνατόν.