Κεφάλαιο 3 Η .NET Framework Class Library

advertisement
.gr
Προγραμματισμός 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 29208
Υπάρχουν περισσότεροι από 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 = (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) 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];
Operate on ArayList and Convert
al.CopyTo (words);
to string[] prior to return
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);
}
Όταν τρέξετε το πρόγραμμα για τον ίδιο του τον κώδικα .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);
Οποιοδήποτε μικρό
γράμμα της αλφαβήτας
Αν βάλετε (@”\”) ο compiler παραπονιέται
Regex regex = new Regex (@"\\");
string[] parts = regex.Split (@"c:\inetpub\wwwroot\wintellect");
foreach (string part in parts) Console.WriteLine (part);
c:
inetpub
wwwroot
wintellect
Επιστρέφει τα 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 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 ();
}
Είναι αρκετά εύκολο να στείλετε 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 = “you@someMailserver.com";
message.To = “AFriend@Mailserver.com";
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.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 ();
collections
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); }
}
}
Μπορούμε να φανταστούμε τις 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;
}
}
Ο compiler προσθέτει
την Attribute
Ο κώδικας μπορεί να χρησιμοποιηθεί ως εξής:
[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;
}
MemberInfo info = typeof (Point);
object[] attributes = info.GetCustomAttributes (false);
Πληροφορίες από
τη Attribute
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: 07-19-2001
Author: steveb
Date: 09-30-2001
Comment: Fixed Bill's bugs
Το Dynamic Loading (δυναμική φόρτωση) αναφέρεται στη διαδικασία εισαγωγής
modules (τυπικά από .dll αρχεία) όταν χρειάζονται. Τα Plug-ins είναι ένα καλό
παράδειγμα του dynamic loading.
Φανταστείτε τα ως βδέλλες που κολλάνε στον κώδικα σας, ή αν προτιμάτε, ως
ενσωματωμένα τσιπ που προσθέτετε στην motherboard ή breadboard σας.
Το Dynamic Loading πραγματοποιείται χρησιμοποιώντας μια τεχνική που λέγεται “Late
Binding”, που ουσιαστικά γίνεται με την αναβολή της ανάθεσης των τύπων (type
assignment) ενός καινούργιου object μέχρι να χρειαστούν πληροφορίες για τους τύπους
(για να πάρουμε τα μέλη τους, το μέγεθος τους, κ.τ.λ.).
Το Reflection κάνει το Late Binding για το Dynamic Loading δυνατόν.
Download