החזרת כל הקבצים בתוך תיקיות משנה ב #C
-
יש לציין שהקוד לא יעיל, הקוד הזה עושה יותר טוב את העבודה והוא פשוט בהרבה:
static void Main(string[] args) { var all = new DirectoryInfo(".").EnumerateFiles("*", new EnumerationOptions { RecurseSubdirectories = true}); foreach (var file in all) Shortcut(file); } /// <summary> /// פונקצית פעולות על כל קובץ /// </summary> public static void Shortcut(FileInfo file) { Console.WriteLine(file.FullName); }
-
@dovid אני רוצה להבין למה הקוד שלמטה והקוד שלך עובדים והקוד של המקורי של @אוריי לא, הרי
Action
הוא סוג שלdelegate
:class search { static void Main(string[] args) { allFiles(new DirectoryInfo("."), new Action<string>(search.Shortcut)); } /// <summary> /// פונקציה להחזרת כל הקבצים בתוך התיקיות /// </summary> /// <param name="direcory"></param> /// <param name="del"></param> public static void allFiles(DirectoryInfo direcory, Delegate del) { direcory.GetDirectories().ToList().ForEach(f => allFiles(f, del)); direcory.GetFiles().ToList().ForEach(f => del.DynamicInvoke(f)); } /// <summary> /// פונקצית פעולות על כל קובץ /// </summary> public static void Shortcut() { Console.WriteLine($"123"); } }
ועוד שאלה, למה להשתמש ב-dynamicInvoke אם הטיפוס של הפונקציה ידועה מראש?
-
@dovid יש לציין גם הקוד הראשון ששיפצת לא עובד הוא מחזיר לי ב cmd את השגיאות הבאות (אע"פ שבויזואל הוא לא מציג לי שום שגיאה)
at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Delegate.DynamicInvokeImpl(Object[] args) at System.Delegate.DynamicInvoke(Object[] args) at ChangeChangeDate.search.<>c__DisplayClass1_0.<allFiles>b__1(FileInfo f) in C:\Users\uri\source\repos\ChangeChangeDate\ChangeChangeDate\Program.cs:line 28 at System.Collections.Generic.List`1.ForEach(Action`1 action) at ChangeChangeDate.search.allFiles(DirectoryInfo direcory, Action`1 act) in C:\Users\uri\source\repos\ChangeChangeDate\ChangeChangeDate\Program.cs:line 28 at ChangeChangeDate.search.<>c__DisplayClass1_0.<allFiles>b__0(DirectoryInfo f) in C:\Users\uri\source\repos\ChangeChangeDate\ChangeChangeDate\Program.cs:line 27 at System.Collections.Generic.List`1.ForEach(Action`1 action) at ChangeChangeDate.search.allFiles(DirectoryInfo direcory, Action`1 act) in C:\Users\uri\source\repos\ChangeChangeDate\ChangeChangeDate\Program.cs:line 27 at ChangeChangeDate.search.<>c__DisplayClass1_0.<allFiles>b__0(DirectoryInfo f) in C:\Users\uri\source\repos\ChangeChangeDate\ChangeChangeDate\Program.cs:line 27 at System.Collections.Generic.List`1.ForEach(Action`1 action) at ChangeChangeDate.search.allFiles(DirectoryInfo direcory, Action`1 act) in C:\Users\uri\source\repos\ChangeChangeDate\ChangeChangeDate\Program.cs:line 27 at ChangeChangeDate.search.Main(String[] args) in C:\Users\uri\source\repos\ChangeChangeDate\ChangeChangeDate\Program.cs:line 16 C:\Users\uri\source\repos\ChangeChangeDate\ChangeChangeDate\bin\Debug\net5.0\ChangeChangeDate.exe (process 31240) exited with code -532462766. Press any key to close this window . . .
-
@yossiz לא יודע. ההערה השניה היא נכונה. זה שיריים של קוד קודם, אכן לא טוב.
@אוריי, שגיאות בקונסול נחשבות שגיאות זמן ריצה, ושגיאות כתיבת הקוד נקראות שגיאת זמן קימפול, או שגיאת קומפילציה.
שגיאה בזמן כתיבת הקוד היא קוד לא תקין תחבירית, ושגיאה בזמן ריצה אומר שהייתה בעיה שהקוד לא נתן עליה את הדעת (Ecxeption Uhandeled - חריגה לא מטופלת. חריגה זו מציאות ספציפית, לא מטופלת זה עצם העובדה הקוד נכשל).
למשל אם אתה מנסה לקרוא קובץ, בזמן קמפול אין שגיאה אם הקובץ קיים או לא, כי יש היתכנות לקיומו והתחביר תקין במאת האחוזים. אם הקובץ לא יהיה קיים בהרצת הקוד, תהיה שגיאת זמן ריצה, זה מה שקרה אצלך (בגלל שבדוגמת הקוד שהבאת, הוא דינמי (DynamicInvoke) וממילא אין אפשרות לאתר את הבעיה בלי להריץ אותו בפועל. זה החיסרון הגדול של הדינמיות שכפי שאמר @yossiz היא הייתה מיותרת פה לגמרי).לעניינו הבעיה היא שהמתודה Shortcut מצפה לקבל string, אז יש לשנות את הact.DynamicInvoke(f) לact.DynamicInvoke(f.FullName) או לתקן את הפרמטר של Shortcut מstring לטיפוס FileInfo.
-
@dovid אמר בהחזרת כל הקבצים בתוך תיקיות משנה ב #C:
אם הייתה מביא את הפלט המלא של הקונסול זה לבטח כלל את השגיאה בשורה הראשונה.
צודק בטעות פספסתי את השורה הראשונה זה הפלט המלא
Unhandled exception. System.ArgumentException: Object of type 'System.IO.FileInfo' cannot be converted to type 'System.String'. at System.RuntimeType.TryChangeType(Object value, Binder binder, CultureInfo culture, Boolean needsSpecialCast) at System.RuntimeType.CheckValue(Object value, Binder binder, CultureInfo culture, BindingFlags invokeAttr) at System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig) at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Delegate.DynamicInvokeImpl(Object[] args) at System.Delegate.DynamicInvoke(Object[] args) at ChangeChangeDate.search.<>c__DisplayClass1_0.<allFiles>b__1(FileInfo f) in C:\Users\uri\source\repos\ChangeChangeDate\ChangeChangeDate\Program.cs:line 28 at System.Collections.Generic.List`1.ForEach(Action`1 action) at ChangeChangeDate.search.allFiles(DirectoryInfo direcory, Action`1 act) in C:\Users\uri\source\repos\ChangeChangeDate\ChangeChangeDate\Program.cs:line 28 at ChangeChangeDate.search.<>c__DisplayClass1_0.<allFiles>b__0(DirectoryInfo f) in C:\Users\uri\source\repos\ChangeChangeDate\ChangeChangeDate\Program.cs:line 27 at System.Collections.Generic.List`1.ForEach(Action`1 action) at ChangeChangeDate.search.allFiles(DirectoryInfo direcory, Action`1 act) in C:\Users\uri\source\repos\ChangeChangeDate\ChangeChangeDate\Program.cs:line 27 at ChangeChangeDate.search.<>c__DisplayClass1_0.<allFiles>b__0(DirectoryInfo f) in C:\Users\uri\source\repos\ChangeChangeDate\ChangeChangeDate\Program.cs:line 27 at System.Collections.Generic.List`1.ForEach(Action`1 action) at ChangeChangeDate.search.allFiles(DirectoryInfo direcory, Action`1 act) in C:\Users\uri\source\repos\ChangeChangeDate\ChangeChangeDate\Program.cs:line 27 at ChangeChangeDate.search.Main(String[] args) in C:\Users\uri\source\repos\ChangeChangeDate\ChangeChangeDate\Program.cs:line 16 C:\Users\uri\source\repos\ChangeChangeDate\ChangeChangeDate\bin\Debug\net5.0\ChangeChangeDate.exe (process 21144) exited with code -532462766. Press any key to close this window . . .
@dovid אמר בהחזרת כל הקבצים בתוך תיקיות משנה ב #C:
כמו"כ בהרצה שלא שלב שלב גם אמור להיות עצירה עם השגיאה, אני לא יודע למה זה לא ככה אצלך.
יכול להיות אולי כי לחצתי על ctrl+f5
-
@yossiz אמר בהחזרת כל הקבצים בתוך תיקיות משנה ב #C:
@dovid אני רוצה להבין למה הקוד שלמטה והקוד שלך עובדים והקוד של המקורי של @אוריי לא, הרי Action הוא סוג של delegate
אם מישהו מעוניין בתשובה לשאלה שימשיך לקרוא...
בקוד של אוריי היו שתי בעיות. הראשונה היא הפשוטה שבמקום להעביר את המתודה
Shortcut
עצמה לפונקציתallFiles
, הוא קרא לפונקציה ובכך העביר את התוצאה שהיא מסוגvoid
.
הבעיה שלי היתה שגם אחרי תיקון בעיה זו, על ידי הורדת הסוגריים הקוד עדיין לא עובד עם הודעה קצת לא מובנת במושכל ראשון:Cannot convert from 'method group' to delegate
. כנראה, הקומפיילר זיהה את הטייפ שלsearch.Shortcut
כ-method group
והוא לא יודע להמיר את זה ל-delegate
.
אבל מה זהmethod group
? ואיך ממירים אותו ל-delegate
?
ועוד יותר מתמיה, למה הקוד של דוד כן עובד? למה אפשר להמירmethod group
ל-Action
ולא ל-delegate
?התשובה היא:
ב-#C יש מושג של function overloading. כלומר פונקציה אחת יכולה להכיל מימושים שונים. ואיך הקומפיילר יודע לאיזה מימוש התכוונת? התשובה היא שכל מימוש חייב לקבל פרפמטרים שונים. הקומפיילר מזהה לפי הפרמטרים לאיזה מימוש התכוונת.
כאשר אתה כותב שם של פונקציה כביטוי, הביטוי לא יכול להיות מסוג delegate כי הביטוי מציין אוסף של פונקציות (מימושים - overloads) שהשם הזה מכיל ו-delegate חייב להכיל רק מימוש קונקרטי אחד. לכן הטייפ של הביטוי הוא 'method group' (טייפ שקיים רק בתוך הקומפיילר ולא בשעת ריצה). לקומפיילר יש כללים איך הוא ממיר method group למימוש קונקרטי. ואחד מהכללים הוא שהוא חייב סימן מזהה מובהק לאיזה מימוש התכוונת וזה על ידי שאתה מצהיר או מרמז על הטייפ של הפרמטרים, שזהו הדבר היחיד שמבדיל בין מימוש אחד לשני.כאשר אנחנו מצהירים ש-allFiles מקבלת
Action<string>
אנחנו מרמזים לקומפיילר שאם נעביר method group כפרמטר, שהוא ידע לבחור מתוכם את זה שמקבל מחרוזת ומחזיר void.
אבל אם הצהרנו ש-allFiles מקבלתDelegate
אין לקומפיילר שום דרך לבחור מתודה אחת מתוך ה-method group שהעברת ולהמיר אותה ל-delegate.כמובן, הקומפיילר היה יכול להיות יותר חכם, ולבדוק אם יש רק מימוש אחד ואז הוא ידע שבטח לזה התכוונת. אבל כיום כנראה הוא לא מספיק חכם.
עוד נקודה שרק עכשיו הבנתי (וכנראה היה פשוט ל- @dovid):
הסיבה שהקוד המקורי השתמש ב-DynamicInvoke הוא משום שהסוג Delegate לא יודע את הטייפים של הארגומנטים שלו. ואם כן בכל מקרה היה שווה להחליף את הטייפ של הארגומט ל-Action
כדי להשתמש בקריאה רגילה סטטית. -
@yossiz תודה רבה על ההסבר, אני אכן אף פעם לא הבנתי עד עתה את השם התמוה Method Group ותודה על כלל ההסבר.
אני מכיר את דוטנט המון שנים, אבל ככותב בVB.NET שמרתי מרחק מהנושאים של דלגייט. אפילו להצהיר על אירוע לא חושב שקרה לי אלא רק האזנה לאירועים של מחלקות אחרות.
כשעברתי לC# (איזור 2012) אז לא ויתרתי על הרבה נושאים, אבל הdelegate היה לי לא מובן, ועד שהייתי צריך אותו הכרתי את הAction והFunc שחוסכים לחלוטין כל צורך בהיכרות ישירה איתו...
לכן כבר כשראיתי את השאלה היה לי לא נח, אבל ברחתי & בחרתי להתמקד באיך אמורים לכתוב.