Wednesday, March 08, 2006

שלום לכולם,

במאמר הבא נבנה ביחד אפליקציה שתנצל את היכולות של System.IO ובעיקר עבודה עם כוננים, ספריות, קבצים כיווץ ו-Isolated Storage.

הרעיון מאחורי ה-System.IO Namespace הוא לרכז את כל העבודה מול Streams ולהציע ספרייה מכובדת שמאפשרת עבודה מול ההארד-דיסק.

המאמר הזה לא עוסק ב-Streams אבל עדיין חשוב שנכיר את המושג. Stream הוא מחלקה אבסטרקטית הנמצאת בתוך System.IO וממנה יורשים כל ה-Streams בפריימוורק. הרעיון מאחורי Stream הוא לאפשר גישה סינכרונית ואסינכרונית לקריאה וכתיבה ממקורות מידע שונים ולרוב בחלקים בגודל קבוע. "בחלקים בגודל קבוע" מתייחס לכך שהקריאה\כתיבה מתבצעת באמצעות buffer בגודל קבוע של בייטים. למשל נחליט לקרוא\לכתוב כל פעם 1024 בייטים. נעבור בקצרה על ה-Streams היותר ידועים: FileStream העוטף את הגישה לקבצים, NetworkStream המקשיב וכותב לתוך סוקט כלשהו ו-MemoryStream שמייצג מידע בזכרון. בנוסף, ישנן מחלקות ה-StreamReader ו-StreamWriter שאחריות על קריאת וכתיבת Streams (בהתאמה). לרוב העבודה מול Streams מוסתרת מאתנו אבל היות ומדובר במאמר על System.IO אז ניתקל בנושא הזה לא מעט וחשוב שנכיר אותו.

נתחיל את הפרוייקט שלנו ונפתח Windows application חדש בשם WinSystemIO. בטופס נוסיף Multiline Textbox בשם tbxOutput שלתוכה נדפיס את המידע שלנו במהלך הפרוייקט ונוסיף כפתור שינקה את תיבת הטקסט. נציין שאין שום חשיבות לכך שיצרנו פרוייקט חלונאי ולא פרוייקט ASP.Net היות ו-System.IO פועלת בשניהם אותו דבר.

 

חלק א': כוננים (או: "מישהו זוכר למה חיכינו לדוט נט 2.0 עד שהוספנו את זה?")

כעת נכיר מחלקה חדשה - System.IO.DriveInfo. המחלקה הזו היא מחלקה קונקרטית המכילה את המידע על כל כונן וכונן במחשב (וגם מאפשרת לשנות חלק מהמידע הזה).  בואו נוסיף לאפליקציה שלנו את האפשרות להציג את האות שמייצגת את הכונן. נשתמש במתודה הסטטית DriveInfo.GetDrives אשר תחזיר מערך של DriveInfo לכל הכוננים במחשב ובמאפיין (גם באנגלית: Property) הנקרא DriveInfo.Name שיאפשר לנו לקבל מכל DriveInfo את האות שמייצגת את הכונן. נוסיף לאפליקציה שלנו כפתור חדש שיבצע את פעולה זו וכך יראה הקוד שלו:

        private void AddNewRow(string TextToAdd)

        {

            tbxOutput.Text += TextToAdd + "\r\n";

        }

 

        private void btnGetAllDriveLetters_Click(object sender, EventArgs e)

        {

            DriveInfo[] AllDrivesOnComputer = DriveInfo.GetDrives();

            foreach (DriveInfo curDriveInfo in AllDrivesOnComputer)

                AddNewRow(curDriveInfo.Name);

        }

הקוד יחסית פשוט - השגנו מערך שמכיל את המידע על כל הכוננים שלנו והוספנו לתיבת הטקסט את האותיות שמייצגות את כל הכוננים.
שימו לב שהשתמנו במתודה AddNewRow שיצרנו שמטרתה היא להוסיף שורה חדשה לתיבת הטקסט. נוסיף להשתמש במתודה זו אז שימו לב לכך.

נראה את התוצאה של לחיצה על btnGetAllDriveLetters:

 

אני יודע מה אתם חושבים ואתם כנראה צודקים - "ג'סטין, יש לך יותר מדי כוננים!".

בואו נעבור על כל התכונות שיש ל-DriveInfo:

  • Name - האות באנגלית שמייצגת את הכונן.
  • IsReady - ערך בוליאני שמחזיר האם הכונן "מוכן". ב"מוכן" הכוונה היא כאשר מדובר בכונן של מדיה נשלפת על האם יש מדיה בכונן. למשל, האם בכונן תקליטורים יש תקליטור בכונן והאם בכונן דיסקטים יש דיסקט בכונן.
  • VolumeLabel - השם שנתנו לכונן. למשל השם של ההארד-דיסק או שם הקומפילציה של התקליטור.
  • DriveFormat - מחרוזת שמייצגת את שיטת הכתיבה על הדיסק, למשל: FAT, FAT32 ו-NTFS.
  • DriveType - ערך Enum שמחזיר את סוג הכונן.

  • TotalSize - סה"כ הגודל של כונן בביטים. אם נרצה לקבל את הכמות ב-kb, mb או gb נצטרך לחלק את TotalSize ב-2 בחזקת המספר המתאים.
  • AvailableFreeSpace/TotalFreeSpace - למעשה מדובר על שתי תכונות נפרדות שמחזירות את המקום הפנוי על הכונן בביטים. יש הבדל ביניהן, אבל הוא יחסית זניח. TotalFreeSpace מחזיר את המקום שבאמת פנוי על הכונן, ו-AvailableFreeSpace מתייחס לכמות הביטים שלמשתמש הספציפי הזה יש הרשאה לכתוב על הכונן. אפשרי שערכי התכונות האלו יהיה שונה, למשל במצב של כונן רשת עם 40gb פנויים אך למשתמש המחובר לרשת יש מכסה למקסימום עד 1gb על הכונן. אבל בעקרון ערכי התכונות האלו לרוב שווים.

חשוב מאוד שנבדוק את ערך ה-IsReadey לפני שננסה להגיע לשאר ערכי ה-DriveInfo, היות ואם למשל אין תקליטור בכונן התקליטורים בטח שלא נוכל לבדוק מה שם התקליטור, כמה מקום הוא סה"כ וכמה מקום פנוי יש עליו. אם בכל זאת ננסה לקרוא למשל כל תכונה של כונן שאינו מוכן נקבל שגיאת זמן ריצה.

טוב, בוא ננצל את כל הידע החדש שהרווחנו כרגע ונדפיס את כל מה שאנחנו יודעים על כל הכוננים.

        private void btnGetAllDriveInformation_Click(object sender, EventArgs e)

        {

            DriveInfo[] AllDrivesOnComputer = DriveInfo.GetDrives();

            foreach (DriveInfo curDriveInfo in AllDrivesOnComputer)

            {

                AddNewRow("***" + curDriveInfo.Name + "***");

                AddNewRow("-------------");

                AddNewRow("Drive Letter: " + curDriveInfo.Name);

                AddNewRow("Drive Type: " + curDriveInfo.DriveType.ToString());

                AddNewRow("Is Drive Ready: " + curDriveInfo.IsReady);

                if (curDriveInfo.IsReady)

                {

                    AddNewRow("Drive Label: " + curDriveInfo.VolumeLabel);

                    AddNewRow("Drive Format: " + curDriveInfo.DriveFormat);

                    AddNewRow("Total Size: " + curDriveInfo.TotalSize);

                    AddNewRow("Available Free Space: " + curDriveInfo.AvailableFreeSpace);

                    AddNewRow("Total Free space: " + curDriveInfo.TotalFreeSpace);

                }

                AddNewRow("-------------");

                AddNewRow("");

            }

        }

החזרנו מערך של כל ה-DriveInfo שיש במחשב, נדפיס את האות שמייצגת את הכונן ואת סוג הכונן. עכשיו נבדוק אם הכונן מוכן. אם הכונן לא מוכן - לא נוכל לבדוק את כל התכונות בתוך התנאי. אם הכונן כן מוכן נדפיס את ערכי התכונות האלו. נעבור להדפיס את הכונן הבא. בוא נראה את התוצאה:

ניתן לראות שכונן E הוא כונן תקליטורים שאין בו תקליטור בכונן, ולכן לא ניסינו להדפיס את שאר הפרטים. אם היינו מנסים, היינו מקבלים  שגיאת IOException עם ההודעה "The Device is not ready".

עכשיו נראה איך להשיג אינפורמציה על כונן שהאות המייצגת אותו ידועה. נוכל להשתמש בקונסטרקטור של DriveInfo ולתת לו את אות הכונן.

        private void btnGetCDriveInformation_Click(object sender, EventArgs e)

        {

            DriveInfo curDriveInfo = new DriveInfo("C:");

 

            AddNewRow("***" + curDriveInfo.Name + "***");

            AddNewRow("-------------");

            AddNewRow("Drive Letter: " + curDriveInfo.Name);

            AddNewRow("Drive Type: " + curDriveInfo.DriveType.ToString());

            AddNewRow("Is Drive Ready: " + curDriveInfo.IsReady);

            if (curDriveInfo.IsReady)

            {

                AddNewRow("Drive Label: " + curDriveInfo.VolumeLabel);

                AddNewRow("Drive Format: " + curDriveInfo.DriveFormat);

                AddNewRow("Total Size: " + curDriveInfo.TotalSize);

                AddNewRow("Available Free Space: " + curDriveInfo.AvailableFreeSpace);

                AddNewRow("Total Free space: " + curDriveInfo.TotalFreeSpace);

            }

            AddNewRow("-------------");

            AddNewRow("");

        }

ותוצאות ההרצה:

 

חלק ב': תיקיות (או: "פן וטלר, קונקרטי וסטטי")

חשוב שנעמוד על ההבדל בין מחלקות סטטיות למחלקות קונקרטיות בחלק זה של הפריימוורק. החלקים הבאים יעסקו בתיקיות וקבצים בכוננים על המחשב. לכל אחד מחלקי System.IO האלו יש מחלקה סטטית המכילה מתודות סטטיות ובנוסף קיימות מחלקות קונטקרטיות המכילות מתודות ומאפיינים. ובפרט, יש את System.IO.Directory המכילה מתודות סטטיות לתיקיות ויש את DirectoryInfo המכילה יצוג קונקרטי של המידע על תיקייה מסויימת. בהמשך נראה שיש גם את File ו-FileInfo. חשוב שנפנים את ההבדל האמיתי בין המחלקות הסטטיות למחלקות הקונקרטיות - המחלקות הסטטיות הן מחלקות סטטיות והמחלקות הקונקרטיות הן מחלקות קונקרטיות. תקראו את המשפט הקודם שוב.

אין הבדל דרסטי ביכולות לבצע פעולה מסויימת ו\או להשיג מידע מסויים בין המחלקות הסטטיות לבין המחלקות הקונקרטיות. מרבית המתודות והאפשרויות קיימות גם במחלקה הסטטית וגם במחלקה הקונקרטית. במאמר זה נציג עבודה גם עם המחלקות הסטטיות וגם עם המחלקות הקונקרטיות.

בואו נתחיל ונבדוק אם התיקייה c:\myDir קיימת:

        private void btnDoesCmyDirExist_Static_Click(object sender, EventArgs e)

        {

            AddNewRow(Directory.Exists(@"C:\myDir").ToString());

        }

 

        private void btnDoesCmyDirExist_Concrete_Click(object sender, EventArgs e)

        {

            DirectoryInfo curDirectoryInfo = new DirectoryInfo(@"c:\myDir");

            AddNewRow(curDirectoryInfo.Exists.ToString());

        }

במתודה הראשונה בדקנו בעזרת המתודה הסטטית Directory.Exist.
במתודה השנייה יצרנו מופע של DirectoryInfo עם כתובת הספרייה ובדקנו בעזרת המאפיין DirectoryInfo.Exist.

נראה את המסך שלנו עם התוצאות:

ראיתם מה עשינו? בדקנו גם באמצעות מתודה סטטית וגם באמצעות מתודה של המחלקה הקונקרטית. נמשיך ונעשה כך לא מעט פעמים במאמר זה.

עכשיו אחרי שראינו שהתיקייה c:\myDir לא קיימת בואו ניצור תיקייה חדשה c:\myDir:

        private void btnCreateCmyDir_Static_Click(object sender, EventArgs e)

        {

            if (!Directory.Exists(@"C:\myDir"))

            {

                Directory.CreateDirectory(@"C:\myDir");

                AddNewRow(@"C:\myDir Created!");

            }

        }

 

        private void btnCreateCmyDir_Concrete_Click(object sender, EventArgs e)

        {

            DirectoryInfo curDirectoryInfo = new DirectoryInfo(@"c:\myDir");

 

            if (!curDirectoryInfo.Exists)

            {

                curDirectoryInfo.Create();

                AddNewRow(@"C:\myDir Created!");

            }

        }

בפשטות, בדקנו אם התיקייה קיימת ואם היא אכן לא קיימת אז יצרנו אותה. אם ננסה ליצור תיקייה שכבר קיימת לא נקבל שום שגיאה, אבל זה עדיין חשוב שנבצע כאלו בדיקות. (הרי הגיוני שאין שום משמעות ליצירת תיקייה שכבר קיימת). בואו נראה את התוצאות:

ואכן נוצרה תיקיית C:\myDir. (תסמכו עליי)

אם אתם מכירים אותי כנראה תדעו שאחרי שיצרתי את הספריית הדגמה הזו כנראה מאוד שאני אשכח למחוק אותה. אז בואו ניצור כאן מחיקת ספרייה שיזכיר לי. נמחוק את הספרייה c:\myDir:

        private void btnDeleteCmyDir_Static_Click(object sender, EventArgs e)

        {

            if (!Directory.Exists(@"C:\myDir"))

            {

                Directory.Delete(@"C:\myDir");

                AddNewRow(@"C:\myDir Deleted!");

            }

        }

 

        private void btnDeleteCmyDir_Concrete_Click(object sender, EventArgs e)

        {

            DirectoryInfo curDirectoryInfo = new DirectoryInfo(@"c:\myDir");

 

            if (!curDirectoryInfo.Exists)

            {

                curDirectoryInfo.Delete();

                AddNewRow(@"C:\myDir Deleted!");

            }

        }

ונראה את התוצאות:

ואכן נמחקה הספרייה c:\myDir. (שוב פעם ,תסמכו עליי)

 

עכשיו בואו נשנה את שם התיקייה מ-C:\myDir ל-C:\myOtherDir:

        private void btnRenameCmyDir_Static_Click(object sender, EventArgs e)

        {

            if (Directory.Exists(@"C:\myDir"))

            {

                Directory.Move(@"C:\myDir", @"C:\myOtherDir");

                AddNewRow(@"C:\myDir renamed to C:\myOtherDir!");

            }

        }

 

        private void btnRenameCmyDir_Concrete_Click(object sender, EventArgs e)

        {

            DirectoryInfo curDirectoryInfo = new DirectoryInfo(@"c:\myDir");

 

            if (curDirectoryInfo.Exists)

            {

                curDirectoryInfo.MoveTo(@"C:\myOtherDir");

                AddNewRow(@"C:\myDir renamed to C:\myOtherDir!");

            }

        }

ונראה את התוצאות:

ואכן שם התיקייה שונה. שימו לב למה שלמעשה עשינו זה "העברנו" את התיקייה c:\myDir לכתובת C:\myOtherDir. שמבחיתנו זה פשוט לתת שם חדש לספרייה.

 

עכשיו בואו נעביר את התיקייה מ-C:\myDir לתוך C:\myOtherDir:

        private void btnMoveCmyDir_Static_Click(object sender, EventArgs e)

        {

            if (Directory.Exists(@"C:\myDir"))

            {

                Directory.Move(@"C:\myDir", @"C:\myOtherDir\myDir");

                AddNewRow(@"C:\myDir moved into C:\myOtherDir!");

            }

        }

 

        private void btnMoveCmyDir_Concrete_Click(object sender, EventArgs e)

        {

            DirectoryInfo curDirectoryInfo = new DirectoryInfo(@"c:\myDir");

 

            if (curDirectoryInfo.Exists)

            {

                curDirectoryInfo.MoveTo(@"C:\myOtherDir\myDir");

                AddNewRow(@"C:\myDir moved into C:\myOtherDir!");

            }

        }

ונראה את התוצאות:

ואכן התיקייה הועברה.

 

עכשיו ניצור בתוך C:\myDir את התיקייה SomeOtherDir:

        private void btnCreateSomeOtherDirInCmyDir_Static_Click(object sender, EventArgs e)

        {

            if (Directory.Exists(@"C:\myDir"))

            {

                Directory.CreateDirectory(@"C:\myDir\SomeOtherDir");

                AddNewRow(@"Created subdirectory C:\myDir\SomeOtherDir!");

            }

        }

 

        private void btnCreateSomeOtherDirInCmyDir_Concrete_Click(object sender, EventArgs e)

        {

            DirectoryInfo curDirectoryInfo = new DirectoryInfo(@"c:\myDir");

 

            if (curDirectoryInfo.Exists)

            {

                curDirectoryInfo.CreateSubdirectory(@"SomeOtherDir");

                AddNewRow(@"Created subdirectory C:\myDir\SomeOtherDir!");

            }

        }

ונראה את התוצאות:

ואכן נוצרה התייקיה C:\myDir\SomeOtherDir.

 

עכשיו בואו נקבל את מערך תת-תיקיות בתוך C:\myDir ונדפיס אותן:

 

        private void btnListSubdirectories_Static_Click(object sender, EventArgs e)

        {

            if (Directory.Exists(@"C:\myDir"))

            {

                string[] SubDirs = Directory.GetDirectories(@"C:\myDir");

 

                foreach (string curDirInfo in SubDirs)

                    AddNewRow(curDirInfo + @" Is a subdir of c:\myDir");

            }

        }

 

        private void btnListSubdirectories_Concrete_Click(object sender, EventArgs e)

        {

            DirectoryInfo curDirectoryInfo = new DirectoryInfo(@"c:\myDir");

 

            if (curDirectoryInfo.Exists)

            {

                DirectoryInfo[] SubDirs = curDirectoryInfo.GetDirectories();

 

                foreach(DirectoryInfo curDirInfo in SubDirs)

                    AddNewRow(curDirInfo.FullName + @" Is a subdir of c:\myDir");

 

            }

        }

ונראה את התוצאות:

ואכן התת-תיקיות של c:\myDir הודפסו. שימו לב להבדל בין המתודה הסטטית Directory.GetDirectories לבין המתודה הקונקרטית DirectoryInfo.GetDirectories. המתודה הסטטית מחזירה מערך של מחרוזות המכילות את הכתובת המלאה של התיקייה, והמתודה הקונקרטית מחזירה מעריך של DirectoryInfo של התת-תיקיות.

אמרנו שכל מה שאפשר לעשות עם מתודות סטטיות של Directory אפשר לעשות עם גם עם מתודו קונקרטיות של DirectoryInfo (ולהפך). זה כל-כך נכון שגם בשביל המאפיינים (באנגלית: Properties) של DirectoryInfo יש מתודות סטטיות. למשל בשביל המאפיין DirectoryInfo.CreateTime שמייצג את תאריך ושעת יצירת התיקייה יש את המתודות Directory.GetCreateTime ו-Directory.SetCreateTime. בואו נראה דוגמה להדפסת תאריך יצירת C:\myDir:

        private void btnGetPermissionsForCmyDir_Static_Click(object sender, EventArgs e)

        {

            if (Directory.Exists(@"C:\myDir"))

            {

                string CreatedDate = Directory.GetCreationTime(@"C:\myDir").ToLongDateString();

                AddNewRow(@"C:\myDir was created on: " + CreatedDate);

            }

        }

 

        private void btnGetPermissionsForCmyDir_Concrete_Click(object sender, EventArgs e)

        {

            DirectoryInfo curDirectoryInfo = new DirectoryInfo(@"c:\myDir");

 

            if (curDirectoryInfo.Exists)

            {

                string CreatedDate = curDirectoryInfo.CreationTime.ToLongDateString();

                AddNewRow(@"C:\myDir was created on: " + CreatedDate);

            }

        }

נביט על התוצאה:

ואכן הודפס תאריך יצירת התיקייה.

ועכשיו בואו נשנה את תאריך ושעת יצירת התיקייה C:\myDir לתאריך והשעה הנוכחיים:

       private void btnChangeCreateDateForCmyDir_Static_Click(object sender, EventArgs e)

        {

            if (Directory.Exists(@"C:\myDir"))

            {

                Directory.SetCreationTime(@"C:\myDir", DateTime.Now);

                AddNewRow(@"Changed C:\myDir creation time to: " + DateTime.Now);

            }

        }

 

        private void btnChangeCreateDateForCmyDir_Concrete_Click(object sender, EventArgs e)

        {

            DirectoryInfo curDirectoryInfo = new DirectoryInfo(@"c:\myDir");

 

            if (curDirectoryInfo.Exists)

            {

                curDirectoryInfo.CreationTime = DateTime.Now;

                AddNewRow(@"Changed C:\myDir creation time to: " + DateTime.Now);

            }

        }

נביט את התוצאה:

ואכן תאריך ושעת יצירת התיקייה שונו.

באופן דומה ל-CreateTime יש גם את DirectoryInfo.LastAccessTime ו-DirectoryInfo.LastWriteTime יש את המתודות הסטטיות המקבילות Directory.GetLastAccessTime, Directory.SetLastAccessTime ו-Directory.GetLastWriteTime, Directory.SetLastWriteTime (בהתאמה).

נביט על שתי מאפיינים של DirectoryInfo שאין להם מקבילם במתודות סטטיות - DirectoryInfo.Attributes ו-DirectoryInfo.Parent. ה

המאפיין DirectoryInfo.Parent מאפשר לנו לקבל את הספרייה שמכילה את הספרייה הנוכחית. אם היינו רוצים לעשות זאת במופע סטטי בלי ליצור עותק קונקרטי של DirectoryInfo היינו צריכים להתחיל לשחק עם מחרוזות. נראה דוגמה להדפסת התיקייה המכילה את התיקייה C:\myDir\SomeOtherDir:

        private void btnGetParentOfCmyDirSomeOtherDir_Concrete_Click(object sender, EventArgs e)

        {

            DirectoryInfo curDirectoryInfo = new DirectoryInfo(@"c:\myDir\SomeOtherDir");

 

            if (curDirectoryInfo.Exists)

            {

                DirectoryInfo ParentDirectoryInfo = curDirectoryInfo.Parent;

                AddNewRow(@"Parent of C:\myDir\SomeOtherDir is: " + ParentDirectoryInfo.FullName);

            }

        }

שימו לב ש-DirectoryInfo.Parent מחזיר לנו DirectoryInfo של התיקייה המכילה את התיקייה הנוכחית.

נביט על התוצאה:

ואכן SomeOtherDir נמצאת בתוך C:\myDir.

 

המאפיין DirectoryInfo.Attributes מאפשר לנו לקבוע האם תיקייה היא נסתרת, לקריאה בלבד, תיקיית מערכת וכל מיני מאפיינים נוספים מעניינים. המאפיין DirectoryInfo.Attributes הוא Enum מסוג FileAttributes. בואו נביט על הערכים האפשריים של ה-Enum:

בואו נקבע את C:\myDir כ-ReadOnly:

        private void btnSetReadonlyForCmyDir_Concrete_Click(object sender, EventArgs e)

        {

            DirectoryInfo curDirectoryInfo = new DirectoryInfo(@"c:\myDir");

 

            if (curDirectoryInfo.Exists)

            {

                curDirectoryInfo.Attributes = FileAttributes.ReadOnly;

                AddNewRow(@"C:\myDir is now readonly!");

            }

        }

נביט על התוצאות:

ואכן C:\myDir הוא עכשיו לקריאה בלבד.

או קיי, ראינו איך ניתן לקבוע תייקיה כ-Readonly וכנראה מאוד שלקבוע את התיקייה רק כ-Hidden או System או כל דבר אחר זאת מטלה מאוד דומה. אבל בואו נביט על תמונת המסך הקודמת ונבקש כזה דבר - תיקייה שהיא גם Readonly וגם Hidden!

אנחנו הרי יודעים שDirectoryInfo.Attributes הוא Enum לכל דבר ואנחנו גם יודעים ש-Enum יכול לקבל רק ערך אחד. הרי אומרים איזה ערך אנחנו רוצים מתוך ערכי ה-Enum וזהו. זה נכון. אבל ניתן לקבוע סוג מיוחד של Enum שנקרא Flags שיכול לקבל מספר ערכים אפשריים של Enum. במקרה לחלוטין FileAttributes הוא באמת Enum מסוג Flags. נפריד את הערכים שאנו רוצים ל-Flags Enum בעזרת התו |. בואו נקבע לתיקייה c:\myDir שהיא גם Readonly  וגם Hidden:

        private void btnSetMultipleAttributesForCmyDir_Click(object sender, EventArgs e)

        {

            DirectoryInfo curDirectoryInfo = new DirectoryInfo(@"c:\myDir");

 

            if (curDirectoryInfo.Exists)

            {

                curDirectoryInfo.Attributes = FileAttributes.ReadOnly

                                            | FileAttributes.Hidden;

            AddNewRow(@"C:\myDir is now readonly & Hidden!");

            }

        }

נביט על התוצאה:

ואכן c:\myDir היא גם קריאה בלבד וגם נסתרת.

בואו נתעקב לשנייה על כל עניין ה-Flags הזה, הרי זה מאוד מעניין. למשל, איך בדיוק אני אומר ש-Enum שלי הוא Flags? ומה המנגנון מאחורי זה שמאפשר לי לבחור מספר ערכים ב-Enum?
בשביל לענות על זה בואו נביט על ההגדרה של FileAttributes.

נתחיל מזה שאנחנו רואים שמדובר public enum. אבל מעל ה-Enum יש את [Flags]. זאת ה-Attribute שמאפשרת לנו מבחינת קומפליציה באמת לבחור כמה ערכים של ה-Enum. אם לא היה את ה-FlagsAttribute מעל ה-Enum לא היינו יכולים לבחור מספר ערכים.

בואו נשים לב לעוד דבר מעניין - ערכי ה-Enum הם כולם 2 בחזקת i. הערך הנומרי שמייצג את Readonly הוא 2 בחזקת 0, הערך הנומרי שמייצג את hidden הוא 2 בחזקת 1, הערך הנומרי שמייצג את System הוא 2 בחזקת 2 וכך הלאה. אבל רגע, למה? בואו נגיד שהערך של איזהשהו הוא DirectoryInfo.Attributes הוא 1, אז ברור שהתיקייה היא Readonly. בואו ונגיד שהערך הוא 2 אז התקייה היא Hidden. בואו נגיד שהערך הוא 3 אז התיקיה Readonly וגם Hidden. אבל ל-System היה את הערך 3 היינו כאן בבעיה, כי DirectoryInfo.Attributes של 3 לא היה יכול להצביע ל-Readonly&Hidden. מה מסתבר? ה-2 בחזקת i מאפשר מספיק "רווחים" בין ערכי ה-enum כך שכל צירוף מספרי שנבחר בהכרח יהיה יצוג לכל היותר לסט אחד ויחיד של ערכי ה-enum.

אז נרצה מתישהו להוסיף Flags לאפליקציה שלנו נכתוב enum רגיל, נוסיף את [Flags] ונקבע את הערכים הנומריים של ה-enum כ-2 בחזקת i.

נסכם את ההסבר על Flags בציטוט של מומחה דוט-נט מיקי ווטס (קישור לבלוג שלו בצד שמאל):

"מכיוון שאנחנו מדברים פה למעשה על דגלים, שלכל אחד יש ערך עם ייצוג בינארי של ביט אחד דלוק וכל השאר מכובים, ניתן למעשה לשלב את הדגלים ע"י הדלקת הביטים הרלבנטיים ע"י שימוש בפעולות מתמטיות בסיסיות. במקרה ויש Enum, ניתן לתת לו Attribute בשם Flags, מה שמאפשר להתייחס אליו כאל שילוב של דגלים."

חלק ג': קבצים (או: "בדיוק כמו קודם, רק עם המילה File במקום Directory ועכשיו יש תוכן")

כמו שעולה מן הכותרת - עכשיו נעשה את כל מה שעשינו קודם עם תיקיות רק עם קבצים. בשביל זה יש לנו את המחלקה הסטטית File ואת המחלקה הקונקרטית FileInfo. נעשה כמה דוגמאות של פעולות בסיסיות וניתן קוד לדוגמה בשימוש גם במחלקה הקונקרטית וגם במחלקה הסטטית. אחרי הכמה דוגמאות הבסיסיות הללו נעבור לקריאה מקבצים וכתיבה לקבצים.

בדיקה האם קיים הקובץ C:\myDir\myFile.txt:

        private void btnDoesmyFileExist_Static_Click(object sender, EventArgs e)

        {

            bool DoesFileExist = File.Exists(@"C:\myDir\myFile.txt");

            AddNewRow(@"Does C:\myDir\myFile.txt exists? " + DoesFileExist.ToString());

        }

 

        private void btnDoesmyFileExist_Concrete_Click(object sender, EventArgs e)

        {

            FileInfo curFileInfo = new FileInfo(@"C:\myDir\myFile.txt");

 

            bool DoesFileExist = curFileInfo.Exists;

            AddNewRow(@"Does C:\myDir\myFile.txt exists? " + DoesFileExist.ToString());

        }

 

יצירת הקובץ C:\myDir\myFile.txt:

        private void btnCreatemyFile_Static_Click(object sender, EventArgs e)

        {

            if (!File.Exists(@"C:\myDir\myFile.txt"))

            {

                File.Create(@"C:\myDir\myFile.txt");

                AddNewRow(@"C:\myDir\myFile.txt Created!");

            }

        }

 

        private void btnCreatemyFile_Concrete_Click(object sender, EventArgs e)

        {

            FileInfo curFileInfo = new FileInfo(@"C:\myDir\myFile.txt");

 

            if (!curFileInfo.Exists)

            {

                curFileInfo.Create();