Download - Προγραμματισμός Windows Κεφάλαιο 3 : The .NET Framework Library (FCL)

Transcript

Προγραμματισμός Windows

Κεφάλαιο 3: The .NET Framework Library (FCL)

Μέρη έχουν παρθεί από το αγγλικό presentation του καθηγητή Dr. Juan Vargas για το μάθημα CSCE 547 τουDepartment of Computer Science and EngineeringUniversity of South CarolinaColumbia, SC 29208

.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 contentsStreamReader 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 stepStreamReader 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 passwordO δειγματικός κώδικας περιγράφει το διάβασμα και γράψιμο δυαδικών αρχείων και κάνει μια μικρή εισαγωγή στην κρυπτογράφηση. Παρατηρήστε τα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 = (1024 + 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<T> { public T Field;}Όταν φτιάξετε μια instance της συγκεκριμένης class, τότε ορίζετε τον πραγματικό τύπο που θα υποκαταστήσει την παράμετρο T:Generic<string> g = new Generic<string>();g.Field = "A string";Στο .ΝΕΤ 2.0 προστέθηκε και η class Collection<T>, με την οποία ορίζετε έναν τύπο αντικειμένων που πρόκειται να αποθηκευτούν στην collection.

Τα Hash tables είναι δομές δεδομένων που αποθηκεύουν ζευγάρια key/values σε θέσεις που λέγονται buckets. Το κυριότερο πλεονέκτημα των hash tables είναι ότι η αναζήτηση είναι πολύ γρήγορη γιατί η «σκληρή» δουλειά γίνεται κατά τη φάση της αποθήκευσης. Στην ουσία, ένα μοναδικό κλειδί αποκτάται για την αποθήκευση των τιμών στα buckets.

Ένας συνηθισμένος κώδικας που χρησιμοποιεί HashTables περιλαμβάνει:HashTable aTable = new HashTable( );aTable.Add( “Sunday”, “Domingo”); // (key,value) pairaTable[“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 ArrayListstring[] 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 wordwhile (i < characters.Length && ! Char.IsLetterOrDigit (characters[i]))

i++;if (i == characters.Length)

return null;int start = i;// Find the end of the wordwhile (i < characters.Length && Char.IsLetterOrDigit (characters[i]))

i++;// Return the wordreturn line.Substring (start, i - start);

}

Operate on ArayList and Convertto string[] prior to return

Όταν τρέξετε το πρόγραμμα για τον ίδιο του τον κώδικα .cs, παίρνετε τα εξής:

113 unique words found in class1.c

... κ.λ.π. ... Καταλαβαίνετε πως πάει.

Το 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 (@"c:\inetpub\wwwroot\wintellect");foreach (string part in parts) Console.WriteLine (part);

c:inetpubwwwrootwintellect

Οποιοδήποτε μικρό γράμμα της αλφαβήτας

Αν βάλετε (@”\”) ο compiler παραπονιέται

Επιστρέφει τα substrings μέσα στο string διαχωρισμένα από το όρισμα μέσα στο RegExp

Για την ανάλυση ενός αρχείου html, με, π.χ.,<b>Every</b>good<h3>boy</h3>does<b>fine</b>

Regex regex = new Regex ("<[^>]*>");string[] parts =

regex.Split ("<b>Every</b>good<h3>boy</h3>does<b>fine</b>");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 “<a[^>]*>”Η παραπάνω εντολή θα εμφάνιζε τις γραμμές μέσα στις οποίες εμφανίζεται το <a> στο αρχείο 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 lineRegex 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 (); }

} }

Είναι αρκετά εύκολο να στείλετε e-mail από ένα .NET Framework πρόγραμμα (αρκεί να έχετε τις σωστές άδειες). Το System.Web.Mail παρέχει ένα managed interface για το SMTP. Οι κυριότερες classes είναι:1.MailMessage, που αναπαριστά τα μηνύματα emails;2.MailAttachment, που αναπαριστά τα συννημένα αρχεία;3.SmtpMail, που χειρίζεται την υπηρεσία μηνυμάτων SMTP του συστήματος.Αυτά είναι τα βήματα για να σταλθούν emails με το System.Web.Mail:

MailMessage message = new MailMessage ();message.From = “[email protected]";message.To = “[email protected]";message.Subject = “What have I done to deserve this?”;message.Body = “Your music was selected for tonight.";SmtpMail.SmtpServer = "localhost"; SmtpMail.Send (message);

<%@ import namespace="System.Web.Mail" %><script language="C#" runat="server">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);}</script><html><body> <h1>Simple SMTP E-Mail Client</h1> <form runat="server"> <hr> <table cellspacing="8"> <tr> <td align="right" valign="bottom">From:</td> <td><asp:textbox id="Sender" runat="server" /></td> </tr> <tr> <td align="right" valign="bottom">To:</td> <td><asp:textbox id="Receiver" runat="server" /></td> </tr> <tr> <td align="right" valign="bottom">Subject:</td> <td><asp:textbox id="Subject" runat="server" /></td> </tr> <tr> <td align="right" valign="top">Message:</td> <td><asp:textbox id="Body" textmode="MultiLine" rows="5" columns="40" runat="server" /></td> </tr> </table> <hr> <asp:button text="Send Mail" onclick="OnSend" runat="server" /> </form></body></html>

Υπάρχει μια πληθώρα από τεχνολογίες Βάσεων Δεδομένων (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 01 2002')";

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.AssemblySystem.Reflection.ModuleSystem.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", "07-19-2001")][CodeRevision ("steveb", "09-30-2001", 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: billgDate: 07-19-2001

Author: stevebDate: 09-30-2001Comment: Fixed Bill's bugs

Πληροφορίες απότη Attribute

Το Dynamic Loading (δυναμική φόρτωση) αναφέρεται στη διαδικασία εισαγωγής modules (τυπικά από .dll αρχεία) όταν χρειάζονται. Τα Plug-ins είναι ένα καλό παράδειγμα του dynamic loading.

Φανταστείτε τα ως βδέλλες που κολλάνε στον κώδικα σας, ή αν προτιμάτε, ως ενσωματωμένα τσιπ που προσθέτετε στην motherboard ή breadboard σας.

Το Dynamic Loading πραγματοποιείται χρησιμοποιώντας μια τεχνική που λέγεται “LateBinding”, που ουσιαστικά γίνεται με την αναβολή της ανάθεσης των τύπων (type assignment) ενός καινούργιου object μέχρι να χρειαστούν πληροφορίες για τους τύπους (για να πάρουμε τα μέλη τους, το μέγεθος τους, κ.τ.λ.).

Το Reflection κάνει το Late Binding για το Dynamic Loading δυνατόν.