אינדקס קודם בLINQ
-
האם יש דרך לפנות בLINQ לאינדקס אחר באותה רשימה? כלומר אני מחפש אוביקט שהתנאים הבוליאנים יתקיימו לפניו ולא עליו עצמו. נניח שאני מחפש אוביקט הבא אחרי אוביקט אחר שיש לו PROP של 3 נניח. זה אמור להיות משהו כמו
var obj = myList.Where(p => p.index-1.prop == 3)
פורסם במקור בפורום CODE613 ב21/12/2016 00:28 (+02:00)
-
זה אפשרי בכמה דרכים שהצד השווה שבהם זה חוסר יעילות.
הדרך שאני עושה בכאלה מקרים: שוכח מLINQ:
myItem GetPrevious(IEnumerable<myItem> collection) { myItem prev = null; foreach (var element in collection) { if(element.MyProperty == "") return prev; prev = element; } return null; }
אם מקור האוסף הוא מערך או ליסט שיש להם גישה מאונדקסת זולה, הקוד יכול להיות קצר ואלגנטי:
myItem GetPreviousIndexed(IList<myItem> collection) { for (int i = 1; i < collection.Count; i++) if(collection[i].MyProperty == "") return collection[i - 1]; return null; }
אפשר לשפץ זאת ליותר גנרי:
T GetPrevious<T>(IEnumerable<T> collection, Func<T, bool> func) { myItem prev = null; foreach (var element in collection) { if(func(element)) return prev; prev = element; } return null; }
הסיבה שבכל המקרים הצגתי זאת כפונקציה זה בגלל האלגנטיות של היציאה ברגע קיום התנאי.
חיפשתי קצת באינטרנט וראיתי שיש כאלה שהשקיעו פיתרון לינקי יעיל: http://stackoverflow.com/a/8760014/1271037 שווה לך להשתמש בזה אם הצורך שלך לא נקודתי אלא חוזר על עצמו.פורסם במקור בפורום CODE613 ב21/12/2016 01:14 (+02:00)
-
דוד למה לא אקסטנשן, זה גם יותר אלגנטי, וכבר היית מלמד אותו עוד משהו על הדרך:
בכל אופן להלן קוד פנטסטי ללא שום מגבלות ביצועים, הוא עושה בדיוק מה שמצופה מלינק לעשות.
ייתכן שהוא לא מושלם, כי אני ממהר ללכת, אם יש באגים אתקן בהמשך.
/// <summary> /// מחזיר רשומות שהרשומות שבאו קודם מקיימות תנאי מסויים /// </summary> /// <typeparam name="TSource"></typeparam> /// <param name="source"></param> /// <param name="predicate"></param> /// <returns></returns> public static IEnumerable<TSource> WhereByPrevious<TSource>(this IEnumerable<TSource> source, Expression<Func<TSource, bool>> expr) { //מכין פרידיקייטים חיוביים ושליליים var predicate = expr.Compile(); var notPredicate = Expression.Lambda<Func<TSource, bool>>(Expression.Not(expr.Body), expr.Parameters).Compile(); //הנותרים כדי שלא לחזור על דברים שכבר עשינו var remaining = source.AsEnumerable(); //קודם כל מדלגים על אלו שלא עונים לתנאי כדי שכשנגיע לתנאי נתפוס את הבא אחריו remaining = remaining.SkipWhile(notPredicate) ; IEnumerable<TSource> result = Enumerable.Empty<TSource>(); do { //לקחת את הראשון בתור שנמצא אחרי שהתנאי מתקיים result = result.Concat(remaining.SkipWhile(predicate).Take(1)); //כעת יש לדלג על זה שלקחנו וגם על השאר שלא מקיימים את התנאי עד שנמצא את התנאי הבא remaining = remaining.SkipWhile(predicate).Skip(1).SkipWhile(notPredicate); } while (remaining.Count()>0); return result; }
פורסם במקור בפורום CODE613 ב21/12/2016 19:52 (+02:00)
-
איזה קטע. אני כתבתי קוד בשביל להביא את איבר לפני קיום התנאי.
לא שמתי לב שרוצים להפך, זה אכן יותר פשוט.
במידה וזה לא בשימוש חוזר כמו שארכיטקט חושב אפשר לעשות תנאי הפוך וכמו שמופיע בקוד שלו SkipWhile ואחריו Skip אחד נוסף:var result = coll.SkipWhile(c => c.MyProperty != "123").Skip(1).First();
ארכיטקט כתב קוד יפה שהופך את התנאי לבד (מוסיף NOT) דבר נוסף הוא מחזיר את כל המקרים ולא רק את הראשון.
לא בדקתי את הקוד. אבל לתשומת לבך ארכיטקט גם הקוד בן השורה שלי ובפרט שלך לא טובים כלל מבחינת ביצועים כלל.כל קריאה לחבר בIEnumerable כמו Skip() או First() ואני לא מדבר על Count מריצה את המניה מחדש.
ייתכן שבמקרה שלכם זה מערך וזה יהיה מהיר אש (אבל עדיין לא נכון מבחינת יעילות)
אבל הרי אתם לא "בונים" על יעילות המניה של האוסף במיוחד אם עושים משהו גנרי, דמיינו שמקור הרשומות זה מסד נתונים או לא יודע מה. לכן כדאי לעשות לולאה פרימיטיבית.
אשכלל את הקוד שלי כדי שיענה על ציפיות הארכיטקט:static class GetRelative { public static IEnumerable<T> GetNextWhere<T>(this IEnumerable<T> source, Func<T, bool> func) { var reader = source.GetEnumerator(); while (reader.MoveNext()) if (func(reader.Current)) if (reader.MoveNext()) yield return reader.Current; } }
אבל חשבתי שאם המטרה זה למצוא את כל המיקומים ולא רק את הראשון, הקוד הן שלי והן של ארכיטקט יפקששו במקרה של תואמים רצופים: הלולאה מוצאת את הראשון מחזירה את זה שאחריו בלי לבדוק אם הוא עצמו תואם ואז צריך להחזיר גם את זה שאחריו...
בשביל זה צריך לשפר את הקוד ככה:static class GetRelative { public static IEnumerable<T> GetNextWhere<T>(this IEnumerable<T> source, Func<T, bool> func) { var reader = source.GetEnumerator(); bool flag = false; while (reader.MoveNext()) { if (flag) yield return reader.Current; flag = func(reader.Current); } } }
פורסם במקור בפורום CODE613 ב21/12/2016 20:33 (+02:00)
-
תודה רבה על כל העצות - וכולנו זכינו כאן להרבה דברים טובים, אמנם לצערי במקרה הזה בכלל אין לינק.
מדובר על פסקאות של מסמך (וורד או אינדיזיין), ושם אין IEnumerable. (וגם לא עוזר המרה, כי הוא לא מקיים את האינטרפייס) ולכן צריך לסרוק אותם אחד אחד, והפתרון הראשון יותר תואם מבחינת הלולאה. אך דא עקא שזה באמת לוקח הרבה זמן, וגם באמת אני צריך גם את מה שלפניו עם כמה וכמה תנאים וגם אם מה שאחריו וכמה וכמה תנאים, וכמובן גם אותו, עם כמה וכמה תנאים, כולם שונים אחד מהשני . (חשבתי את התנאי הקשור לפסקה המרכזית אוכל בנקל להכניס ללינק והיה חסר לי הענין של האינדקס). ובאמת הוספתי לטיפוס המרכזי אקסטנשן כדי שיהא יותר נח לכתוב, אך גם בזה יש מתודות אבל לא פרופרטיז.
אז כדי לחסוך בזמן בניתי מחלקה עוטפת מותאמת אישית של PARA, ולה יש PROP של current, next, prev בהתאמה.
ומטעמי חסכון אני עובר בלולאה אחת על הכל, ואם מצאתי משהו בתנאי המרכזי, כלומר בפסקה בנוכחית, אני מכניס אותו, ואת הקודם לו ואת הבא אחריו לאובקיט, ועושה מהם ליסט חדש.
על ליסט, יש לינק, ואז זה ממש PROP רגילים שעושים עליהם לינק בלי קשר למקום שלהם ברשימה. ואחרי זה אני מפעיל את האופרציה הנדרשת על הרשימה המסוננת מכל וכל. ככה גם הקוד קריא יותר, כי לשים באותה לולאה גם את התנאים על כל שלושת החברים עושים בלאגן גדול.פורסם במקור בפורום CODE613 ב22/12/2016 00:04 (+02:00)
-
דוד Skip() ממש לא מריץ מחדש. הוא עומד היכן שהוא ומדלג.
לגבי דטה בייס עושים את זה עם LAG, ואני לא יודע אם אנטיטי פריימוורק יודע לעשות זאת.ידידי אתה טועה.
אתה יכול לבדוק זאת פשוט, שים ברייקפוינט או לוג.IEnumerable<MyClass> GetAllBlaBla() { Console.WriteLine("start"); for (int i = 0; i < 100; i++) { //add here Thread.Sleep to simulate I/O yield return new MyClass { MyProperty = i }; } }
תראה במקרה שלך זה ידפיס המון פעמים start וחלק מתוכם זה יעשה את כל הלולאה בלי להפסיק.
אם זה DB זה יריץ כמספר הזה גם שאילתות. תמיד אפשר להמיר לפני זה למערך אבל זה בוגד בלינק - ומסתכן גם בפגיעה ביעילות בזיכרון.
לא מכיר מה זה LGA.פורסם במקור בפורום CODE613 ב22/12/2016 00:38 (+02:00)
-
-
נתנאל הקוד בסטאק הוא בדיוק בשבילך הן לקריאות והן לביצועים.
הרעיון הוא:- להימנע מלעבור על הפסקאות יותר מפעם אחת כי זו הפעולה היקרה
- לאפשר מוניפולציות מחוץ לקוד המעבר אבל שכן יבוצעו בשעת המעבר בשביל לחסוך "לשמור" וגם לבדוק דברים פעמיים.
מחר אראה לך דוגמה בס"ד.
פורסם במקור בפורום CODE613 ב22/12/2016 00:55 (+02:00)
-
הנה הדוגמה. הקלאס PrgClass מייצג פסקה, החלף את זה למה שרלוונטי לעניינך.
IEnumerable<Entry> GetAll() { PrgClass a = null; PrgClass b = null; PrgClass c = null; foreach (var element in xyz) //פה הלולאה על הפסקאות של וורד { a = b; b = c; c = element; yield return new Entry { Before = a, Current = b, After = c } ; } } class Entry { public PrgClass Before { get; set; } public PrgClass Current { get; set; } public PrgClass After { get; set; } }
מה המעלה בקוד הזה?
שאחרי זה אם אני כותב:var coll = GetAll(); coll = coll.Where (x => x.After == null || x.After.x > 5); coll = coll.Where(x => x.Before != null && x.Before.y == 6); coll = coll.OrderBy (x => x.Current.y); var result = coll.First();
אפי' שהעברתי תנאים בכמה שלבים, ואחרי זה בוצע סידור כל זה מתבצע במעבר אחד על הפסקאות.
כלומר הקוד הזה מתבצע כאילו כתבו את כל ההתניות והפרמטרים בגוף הלולאה, אף שזה מופרד וממילא הרב יותר נוח.
במה זה יותר טוב מאשר לייצא ידנית לליסט? א. יעילות עיבוד - אפי' שאפשר להוסיף כמה התניות בקטעי קוד שונים אין סריקה מחדש של האלמנטים - הכל מתבצע באותה הסריקה. ב. יעילות זיכרון. שכל אלמנט שכבר אין בו צורך (תוצאה שלילית על אחד מכל התנאים שיהיו) אזי הוא "נשכח" ונאסף ע"י אוסף הזבל אפי' תוך כדי הלולאה.
במה זה טוב מהתניות בגוף הלולאה? הרבה יותר נוח בגלל שאפשר לפצל את הקוד שמייצר את האלמנטים לבין ההתניות שאפשר להוסיף במקומות שונים עד לייצוא.פורסם במקור בפורום CODE613 ב23/12/2016 12:17 (+02:00)
-
תודה רבה! אני עדין באמצע לעכל את כל העושר התכנותי שמתעופף כאן בנדיבות.
כפי מה שהבנתי מהקוד, כל הענין בנוי על yeild שהוא בעצם מאפשר לנו להפסיק כל לולאה או כל אינומרבל ברגע שאנו לא צריכים אותו יותר, וכן ליצור אותו רק כשצריך. כמו כן זה עובד על זה שחלק מהאופרטורים של לינק עובדים על זה ג"כ שהם לא נקראים עד שהם נצרכים ובינהם Where. לכן נוצר מצב בו אני יכול להצהיר על אינוברבל, במקרה הזה הפסקאות עצמם, והיצירה שלו תהיה "באוויר" עד שיצטרכו לעשות איתו משהו, וזה ההפעלה של התנאים, ואז שעוברים עליו בפעם הראשונה, בכלל זה "לוקחים" בחשבון את התנאים, ולכן יוצא שהמעבר הוא רק פעם אחת.
אבל אם כן, לא הבנתי, למה צריך את yeild בפעם הראשונה. הרי בין כה וכה נצטרך לעבור על כל הפסקאות כדי לעבור עליהם כדי לראות אם הם מקימים את התנאים, ונמצא באמת אף פעם לא מופסק באמצע היצירה של האינמרבל. אבל אם היה תנאי עצירה למשל של רק כמה פסקאות במספר מסוים אז אני מבין מדוע היה צריך את זה כדי שלא נעבור על יותר ממה שצריך.
וגם, מה יהיה הדין אם נצטרך להשתמש באופרטורים "חמדניים" כמו ספירה או מינמום, אז באמת הכל יצטרך להתבצע מיד, ותנאים נוספים יתנו תוצאות שקריות מכיון שהאוסף עבר מניפולציה כלשהיא.List<string> ms = new List<string>() { "1","2","545","5445", "reewe" ,"qerwqerqwewrrwer"}; IEnumerable<string> result = ms.Where(o => o.Length > 3); int num = ms.Where(o => o.Length > 3).Count(); ms.Add("erwereeweweweeeeeeeesdsddddddddd"); foreach (var t in result) { Console.WriteLine(t); } Console.WriteLine(num);
הקוד הזה יחזיר 4 תוצאות, אבל ספירה של 3.
ובר מן דין , אין לי אפשרות להעביר את אוסף הפסקאות למשהו אינמרבל, כיון שהוא לא מקיים את האינטרפיס, ועל כן אני חייב לעשות את ההמרה הראשונה לאינמברל ידני לליסט.
וענין הפרדת הקוד סריקה והקוד תנאים, אני משתמש בדיקצנרי, שצד אחד שלו זה קלט מהתמשמש, סטרינגי בדרך כלל והצד השני שלו זה Func.
לכל תנאי כזה יש דיקצנרי משלו עם הפעולות שלו, ולחלקם יש פשוט return true אם לא נבחרה אופרציה מסוימת. אחרי זה אני משרשר אותם יחד ומפעיל. וככה זה יותר מופרד יחסית, כי בסריקה אני רק מציין לדלגציה שמופיעה בדיצקנרי. (וכדי לחסך בזמן אני רוצה שטעינה של המילונים תהיה אסנכורנית).פורסם במקור בפורום CODE613 ב24/12/2016 22:14 (+02:00)
-
כפי מה שהבנתי מהקוד, כל הענין בנוי על yeild שהוא בעצם מאפשר לנו להפסיק כל לולאה או כל אינומרבל ברגע שאנו לא צריכים אותו יותר, וכן ליצור אותו רק כשצריך. כמו כן זה עובד על זה שחלק מהאופרטורים של לינק עובדים על זה ג"כ שהם לא נקראים עד שהם נצרכים ובינהם Where. לכן נוצר מצב בו אני יכול להצהיר על אינוברבל, במקרה הזה הפסקאות עצמם, והיצירה שלו תהיה "באוויר" עד שיצטרכו לעשות איתו משהו, וזה ההפעלה של התנאים, ואז שעוברים עליו בפעם הראשונה, בכלל זה "לוקחים" בחשבון את התנאים, ולכן יוצא שהמעבר הוא רק פעם אחת.
נכון.
אבל אם כן, לא הבנתי, למה צריך את yeild בפעם הראשונה. הרי בין כה וכה נצטרך לעבור על כל הפסקאות כדי לעבור עליהם כדי לראות אם הם מקימים את התנאים, ונמצא באמת אף פעם לא מופסק באמצע היצירה של האינמרבל. אבל אם היה תנאי עצירה למשל של רק כמה פסקאות במספר מסוים אז אני מבין מדוע היה צריך את זה כדי שלא נעבור על יותר ממה שצריך.
- הרבה פעמים מחליטים "לעצור" בפעם הראשונה. למשל Take. או First, Any ועוד.
- השתלת כל התנאים והמיונים באותו המעבר - הראשון. אם אתה מחזיק איבר ביד, למה שלא תבדוק אם הוא עונה כבר על התנאים?
- חיסכון החזקת האיברים באיזה מקום - זיכרון. למשל קוד שיקרא רשימת קבצי טקסט ויחזיר אותם בyield וקוד אחר יבקש את שם האיבר/קובץ שמופיע בו מילה מסויימת - בשום שלב הזיכרון לא צריך "לסבול" יותר מתוכן של קובץ בודד.
וגם, מה יהיה הדין אם נצטרך להשתמש באופרטורים "חמדניים" כמו ספירה או מינמום, אז באמת הכל יצטרך להתבצע מיד, ותנאים נוספים יתנו תוצאות שקריות מכיון שהאוסף עבר מניפולציה כלשהיא.
לIEnumerable יש כמה מתודות שמחזירות ערך יחיד (Scalar Value) למשל מזכרוני:
Count, Sum, Avarage, Any, All, First, Last, Single,
כל אלה מפעילות את המניה מיידית - ולמעשה מסיימות את שרשרת הIEnumerable.
לא שייך תוצאות שקריות כי ממילא א"א לשרשר אחריהם - הם לא מחזירים אוסף.כמו"כ יש כמה מתודות ייצוא שמשמעותיות למקרה ויידרשו לגשת כמה פעמים לאוסף ואז יש לחסוך במניה (שיכולה להיות מתורגמת לפעולות רציניות מאוד כמו קריאה מDB וכל מה שיכול לעלות בדעתו של מספק האוסף) מיותרת:
ToArray, ToList, ToDictionary, ToLookupובר מן דין , אין לי אפשרות להעביר את אוסף הפסקאות למשהו אינמרבל, כיון שהוא לא מקיים את האינטרפיס, ועל כן אני חייב לעשות את ההמרה הראשונה לאינמברל ידני לליסט.
ממש ממש לא. אם המפתח שמספק את האוסף נתן לך List אז אין מה להמיר.
ואם הוא נתן אוסף שאינו מממש את הIEnumerabl הרי אפשר עדיין לעבור עליו בforeach ושם לשים את הyield. אכן ייתכן שאין הבדל בעלות של הפעלת הforeach ועצירתו מיידית לבין הפעלותו עדך הסוף - זה בהתאם לרמת המפתח שסיפק את האוסף.אגב הyield הוא צעיר מלינק. לפני קיומו היו כותבים מחלקה קצת ארוכה שהוציאה את החשק להשקיע.
גם כיום הקומפיילר מתרגם כל מתודה שמחזירה yield למחלקה בפני עצמה שיורשת מIEnumerable ובתוכה מחלקה של IEnumerator.
לIEnumerator יש מאפיין בשם Current שמחזיר איבר נוכחי, ומתודה בשם MoveNext שקוראת לאיבר הבא (ומחזירה שלילי בסיום). למעשה זה כל תורת לינק - לתת לסורק האוסף את השליטה מתי לזוז ומתי לפרוש.פורסם במקור בפורום CODE613 ב25/12/2016 01:15 (+02:00)
-
ישר כח גדול!
אין אפשרות לגשת ישירות לאוסף הפסקאות בלינק. משהו כזה לא עובד:var test = doc.Paragraphs.where(p=> p.Text=="tt")
וגם אי אפשר להמיר את זה לליסט לא ידני - אבל בהחלט לעבור על זה בforeach עובד גם עובד! (היה מונח לי בראש משום מה שרק מה שאפשר לעבור עליו עם foreach סובל לינק, והפוך, אבל אני מבין כרגע שזה לא תלוי זה בזה לגמרי).
ושוב תודה!פורסם במקור בפורום CODE613 ב25/12/2016 01:31 (+02:00)
-
אכן, אתה צודק ואני טעיתי כמה טעויות...
אין foreach בלי IEnumerable, ואכן הParagraps של וורד כן מממש IEnumerable.
אלא שמתודות ההרחבה של IEnumerable (כמו Wher וכל היתר) עובדים רק במימוש IEnumerable<T> כלומר הגירסה הגנרית.
ופה הטעות הגדולה: צריך סה"כ להמיר מIEnumerable לגירסה הגנרית, ובשביל זה אתה בכלל לא צריך לכתוב את הforeach עם yield.
יש שתי מתודות שממירים IEnumerable מסוג כלשהוא או ללא סוג כלל כמו אצלנו לאוסף גנרי מסוג אחר. אחד Cast והשני TypeOf.
שניהם ממומשים פשוט עם לולאה על האוסף עם yield, הCast פשוט מציב סוגריים לפני האיבר ומנסה לעשות קאסטינג - כלומר הוא מניח שזה בודאי מהסוג שציינו. ואילו הTypeOf בודק על כל איבר באמצעות is אם זה הסוג שצויין ומחזיר רק איבר שהתשובה לגביו חיובית.
במקרה שלך Cast זה מעולה. אתה פשוט כותב ככה:var test = doc.Paragraphs.Cast<Microsoft.Office.Interop.Word.Paragraph>(); test = test.Where (t => t.Range.Text.Length > 200);
פורסם במקור בפורום CODE613 ב25/12/2016 10:44 (+02:00)
-
מצוין, זה ממש פתח כפתחו של אולם. מרחיב לחלוטין את לינק. אבל במקרה שלנו אני אהיה חייב לא להתשמש באוסף כמות שהוא אפ' שאפשר לעשות עליו לינק, מכיון שאני רוצה ליצור ממנו טיפוס חדש שמכמס בתוכו את הפסקה לפני ואחריו וnull אם אין משהו לפני או אחרי, וא"כ הגם שforeach עם yeild יתן לי את האוסף כמו שהוא, אבל עדיין לא יהיה לי גישה לאברים לפני ואחרי(אא"כ סקיפ וכו'), ולכן אני משתמש בFor, גם עם yeild , וככה יש לי גם גישה לאינדקס, ולהכניס לקונסטרקטור את הפסקאות לפני ואחרי, וגם את האקטסנשיין, מכיון שהם לא פועלות על הפסקאות עצמם אלא על הטיפוס החדש, צריך לכתוב על הטיפוס החדש ולא על טיפוס הפסקאות של וורד.
פורסם במקור בפורום CODE613 ב25/12/2016 11:12 (+02:00)
-
מצוין, זה ממש פתח כפתחו של אולם. מרחיב לחלוטין את לינק. אבל במקרה שלנו אני אהיה חייב לא להתשמש באוסף כמות שהוא אפ' שאפשר לעשות עליו לינק, מכיון שאני רוצה ליצור ממנו טיפוס חדש שמכמס בתוכו את הפסקה לפני ואחריו וnull אם אין משהו לפני או אחרי, וא"כ הגם שforeach עם yeild יתן לי את האוסף כמו שהוא, אבל עדיין לא יהיה לי גישה לאברים לפני ואחרי(אא"כ סקיפ וכו'), ולכן אני משתמש בFor, גם עם yeild , וככה יש לי גם גישה לאינדקס, ולהכניס לקונסטרקטור את הפסקאות לפני ואחרי, וגם את האקטסנשיין, מכיון שהם לא פועלות על הפסקאות עצמם אלא על הטיפוס החדש, צריך לכתוב על הטיפוס החדש ולא על טיפוס הפסקאות של וורד.
קוראים לזה projecting.
גם זה אפשר בlinq. המתודה Select (ולמעשה עוד כמה מתודות) מחזירים טיפוס חדש. למעשה זה בדיוק קיצור למה שעשית - foreach עם yield.
דוגמה לselect:var coll = new int[] { 1, 2, 23, 4}; var query = coll.Select(c => new MyClass(c));
אבל בשביל הקודם והבא לדעתי אתה צריך לכתוב לולאה מפורשת כמו שכתבתי בכמה אופנים.
כל הדוגמאות של שימוש במתודות ההרחבה נקראות Fluent Syntax, בשביל הקריאות כדאי לפעמים להשתמש בquery syntax הנה דוגמה:
var collAsMyclass = from item in coll where item > 2 select new MyClass(item); var filtered = from item in collAsMyclass where item.MyProperty > 15 select item;
פורסם במקור בפורום CODE613 ב25/12/2016 12:22 (+02:00)
-
מה שכתבתי על אסקטנשיינס, התכוונתי לטיפוס החדש שעשיתי, שלא אצטרך כל פעם להכניס אותו אישית לפונקציה כדי לבדוק אותו. לא בסינטקס של לינק. אבל עדיין עם foreach אין לי גישה ללפני ואחרי מבחינת האינדקס. וגם הכתיבה צורכת התניות של ראשון ואחרון. עם for אני יכול לגשת לקונסטקטור בשקט בשורה אחת. אם זה היה עם foreach לא היתה לי גישה ישירה כפרפרוטי לקודם.
public IEnumerable<mypara> GetAllParas(word.Document doc) { for (int i = 1; i <= doc.Paragraphs.Count; i++) { yield return new mypara(doc.Paragraphs[i], i == 1 ? null : doc.Paragraphs[i - 1], i == doc.Paragraphs.Count ? null : doc.Paragraphs[i + 1]); }
ובאמת הוא אותו דבר, שגם "נצרך" על ידי המערכת רק ברגע הסינון הסופי של התניות.
פורסם במקור בפורום CODE613 ב25/12/2016 12:43 (+02:00)