C# - הוספת פעולת Poperty מאיבר לאיבר כשהם מקוננים ב List בתוך List.
-
אני כעת עובד על המצאת הגלגל מחדש:)
מנסה ליצור קלאס שידמה טבלת SQL, וכל הפעולות שיפעלו על הקלאס ישתקפו בטבלה. (כהמשך לנסיון פתרון השאלה כאן)אז, הדרך שחשבתי הוא ליצור שדה בשם
// private List<List<object>> dataTable; Public List<List<object>> DataTable {get; set;}
אך אני צריך שברגע שישתנה ערך בתוך DATATABLE אוכל לעדכן את הקלאס שהשתנה ערך. (בעצם LIST אחר שכל איבר בו משמש לאחסון הנתונים: שם הטבלה, שם העמודה, וערכי שדות ה ID, או - ליצור מיד UPDATE / DELETE / INSERT).
איך אני ניגש ל PROPERTY של הערכים בתוך ה LIST?
אם אי אפשר הברירה היחידה לכאורה תהיה ליצור מתודה לכל שינוי, אבל אני מנסה להבין לעומק יותר, ולפשט יותר, האם אפשר ליצור את התיעוד של השינוי מעצם שינוי המשתנה?
(רעיון שחשבתי, קצת מופרע אבל אולי זה הכיוון, ליצור קלאס היורש מ LIST<LIST<OBJECT>> ולעשות override
נ.ב. גם אם הדרך לעשות את זה היא לא כמו שאני מנסה, תוכלו במקביל לתשובה על השאלה להכניס את זה בהערת אגב, אבל בבקשה לא להסיט את הנושא לאיך כדאי לפתח.
-
@Y-Excel-Access כתב בC# - הוספת פעולת Poperty מאיבר לאיבר כשהם מקוננים ב List בתוך List.:
(כהמשך לנסיון פתרון השאלה כאן)
אופס, שכחתי לענות לך שמה...
יש מחלקה בשם DataTable, מהשם שבחרת נראה שבדקת אותה כבר? היא בסדר והיא עושה מה שרצית לעשות והיא די דינמית.
בא ניגש לתפישת העיצוב שלך:
עשית ליסט של שורות, כל איבר בה היא ליסט של התאים/שדות, וכל איבר זה אובייקט כלומר ערך גנרי מספר/טקסט וכדומה.יש לנו INSERT + DELETE שהם ברשמת שורה בליסט העליון (לעקוב אחריו באמת אי אפשר, צריך סוג של ליסט אחר בשם ObservableCollection אבל כיון שזה פעולות מאוד מצומצמות אז אפשר על ידי מתודה מיוחדת כי שכתבת).,
UPDATE הוא היחיד שהוא ברמת שינוי האובייקט/התא.
בשביל לעקוב אחרי שינוי ברמת התא אתה צריך לתפוש השמה לתוך הList.
כלומר הדרך היחידה לערוך את התא זה ע"י הצבה בList התחתון (כלומר רמת התאים)// int row = 50; int column = 2 DataTable[row][column] = 123;
אתה יכול לעטוף את זה. כלומר לעשות מתודה אחת שהיא מעדכנת:
void UpdateCell(row, cell, val) { var oldVal = DataTable[row][cell]; DataTable[row][cell] = val; //run sql update.. }
כמובן שאתה יכול לאכוף את זה יותר חזק על ידי שהList יהיה List מיוחדת לקריאה בלבד (זה אכיפה שהמפתח כלומר אתה בעצמך לא תעדכן באיזה מקום בטעות שלא דרך המתודה. בשפות עיליות רוב האנרגיות היא נגד (טעויות של) עצמך:) ), ואתה יכול לחילופין לעשות מחלקה משלך שמייצגת שורה ואז יכולו לעדכן איך שרוצים ואתה תידע מזה:
Class Row { //.... initalize all fields UpdateCell(string field) => .... UpdateCell(int field) => .... }
במקרה הזה אתה יכול גם לשלוט על האינדקסר כלומר מה יקרה כשכותבים row[x,y] ושגם אז זה יעדכן ושתדע מזה ותפעיל את השאילתה המתבקשת.
כל זאת כתבתי לשם הפלפול כבקשתך בסוף השאלה...
כי כמו שהערת אני בהחלט חושב שלא צריך לא את זה ולא הDataTable של דוטנט (שעושה מעולה מה שחשבת). אתה צריך לקחת ממני כמה שיעורים כי אתה מידי רחב בשביל שו"ת... -
@dovid כתב בC# - הוספת פעולת Poperty מאיבר לאיבר כשהם מקוננים ב List בתוך List.:
מובן שאתה יכול לאכוף את זה יותר חזק על ידי שהList יהיה List מיוחדת לקריאה בלבד (זה אכיפה שהמפתח כלומר אתה בעצמך לא תעדכן באיזה מקום בטעות שלא דרך המתודה. בשפות עיליות רוב האנרגיות היא נגד (טעויות של) עצמך:) ), ואתה יכול לחילופין לעשות מחלקה משלך שמייצגת שורה ואז יכולו לעדכן איך שרוצים ואתה תידע מזה:
Class Row{ //.... initalize all fields UpdateCell(string field) => .... UpdateCell(int field) => ....}במקרה הזה אתה יכול גם לשלוט על האינדקסר כלומר מה יקרה כשכותבים row[x,y] ושגם אז זה יעדכן ושתדע מזה ותפעיל את השאילתה המתבקשת.
תודה רבה על התשובה המדוייקת, אבל לא הבנתי את דרך היישום.
דווקא היה לי כמה נסיונות עקרים ליצור מחלקה משלי, אבל עדיין המחלקה לא יכולה לצאת מתוך עצמה למתודה אחרת בקלאס הראשי.
אם יש לי 3 מחלקות: TABLE (דרך אגב לא הכרתי את ה DATATABLE של NET), ROWS ו COLUMNS, כך:Public class Table { Public Rows MyRows; } Public class Rows : List<Columns> { } Public class Columns: List<object> { }
איך אני יכול על ידי יישום ROWS ב TABLE לגעת ב GET ו SET הפנימי של COLUMS? איך האוביקט מסוג COLUMNS שנוצר יוכל לצאת ממחלקתו ולשנות משהו בTABLE?
מקווה שהובנתי, אם לא אפשר לשאול מה התכוונתי.
-
@Y-Excel-Access כתב בC# - הוספת פעולת Poperty מאיבר לאיבר כשהם מקוננים ב List בתוך List.:
איך אני יכול על ידי יישום ROWS ב TABLE לגעת ב GET ו SET הפנימי של COLUMS? איך האוביקט מסוג COLUMNS שנוצר יוכל לצאת ממחלקתו ולשנות משהו בTABLE?
לא הבנתי למה זה נדרש.
אם יש מחלקה תלויה, התקשורת בינה לבין האחרות נעשה ע"י אירועים ופרמטרים שבד"כ מועברים לבנאים.
אתה לומד את יסודות C# + OOP בדרך אגב של בניית אפליקציה די מתקדמת, זה מצד אחד טורבו רציני ללימוד, מצד שני שים לב שאתה נוסע על 240... אל תתפלא אם תעוף באיזה ג'אמפרניסיתי לכתוב קוד דומה לשלך. ויתרתי על הRows כי לכאורה הוא סתם List רגיל.
הנה התוצאה, בה יש תקשורת בין השורה לטבלה.public class Table { public Table(string[] _columnsNames) { columnsNames = _columnsNames; MyRows = new List<Columns>(); Rows = MyRows.AsReadOnly(); } private List<Columns> MyRows; public IReadOnlyCollection<Columns> Rows; public Columns AddRow() { var cols = new Columns(columnsNames); MyRows.Add(cols); return cols; } public string[] columnsNames; } public class Columns : List<object> { public object this[int index] { get { return base[index]; } set { Console.WriteLine($"פה אתה יודע שהולך להשתנות תא. שם התא: '{columnsNames[index]}' ערך קודם: '{base[index] ?? "<ריק>"}' ערך חדש: '{value ?? "<ריק>"}'"); base[index] = value; } } string[] columnsNames; public Columns(string[] _columnsNames) { columnsNames = _columnsNames; foreach (var col in columnsNames) this.Add(null); } }
שימוש:
var t = new Table(new[] { "שם", "שם משפחה" }); var row = t.AddRow(); row[0] = "דגים";
-
@dovid תודה רבה! אני לומד את הקוד.
בכוונתי להתנסות ולהמשיך וללמוד את זה.
במקביל אבקש שתי בקשות.-
האם אפשר לקבל קישור לאתר (מייקרוסופט?) שמסביר על האופציה של "לתפוס" את הINDEX של איברי המערך / LIST בדומה למה שביצעת כאן, וייתן לי מבט מעמיק יותר?
או אפילו מילים לחיפוש בגוגל שייתנו לי את התוצאות הקשורות ולא יסיטו אותי לנושאים מגוונים אחרים - איך קוראים לנושא שלנו באנגלית תכנותית? -
אם זה קל מבחינתך - האם אפשר גם לעשות אופציה של
Public class Table { public Rows Values; } public class Rows:List<List<object>> { }
כלומר ליצור אובייקט LIST מקונן ב LIST ובו לשלוט.
ואולי אפילו לוותר בכלל על קלאס ROWS.כך שהשימוש יוכל להיות גם כך
/* אתחול. לא על זה השאלה ואני כבר פיתחתי את זה. אבל כאן אני רק מסביר מה כוונתי - אני מקבל את שם הטבלה ואני מתחבר אל הנתונים שלה, ואח"כ אשנה את הנתונים בערכים שבטבלה וממילא יווצר Update Insert Delete. */ List<string> columnsNames columnsNames.Add("name"); columnsNames.Add("family name"); var t = new Table(ColumnsNames:= columnsNames, TableName:= "Table1" , ConnectionText:= "..............."); // שימוש - זהו האופציה שרציתי לקבל: t.Values[0][2] = "tttttt";
-
-
@Y-Excel-Access
אנ עונה לך על שאלה 1:
לינק לתיעוד על אינדקסר:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/indexers/
מילים לגוגל C# indexer -
לגבי שאלה 2, זה לא נצרך. אתה יכול עכשיו לכתוב קוד זהה לזה, רק תשנה את הIReadOnlyCollection לReadOnlyCollection, ותוכל לכתוב ככה:
table.Rows[0][2] = "tttttt";
תוכל להוסיף אינדקסר גם לtable עצמה וככה לגשת ישירות אפילו בלי לעבור דרך השדה Rows.
אינדקסר יכול לטפל גם בטקסט, כך שתוכל לכתוב קוד מתאים לפניה לפי שם ולא לפי אינדקס. -
@dovid כתב בC# - הוספת פעולת Poperty מאיבר לאיבר כשהם מקוננים ב List בתוך List.:
רק תשנה את הIReadOnlyCollection לReadOnlyCollection
הוא רושם לי שאין כזה קלאס / אינטרפייס.
-
@Y-Excel-Access זה בגלל שחסר הפניה למרחב השמות הנדרש (לא זוכר מה הוא).
מתי שיש שגיאה יש קו אדום למטה + סמל בצד של נורה.
אם אתה לוחץ עליה אתה יכול לבחור בפתרון להוסיף למסמך using למה שצריך.
תוכל לפתוח את תפריט ה"נורה" הזאת גם בקיצור של Ctrl+ץ כשאתה עומד בשורה שעושה את הצרות. -
-
@קומפיונט העיר שלפי פילוסופיית הOOP לא הגיוני לעשות ירושה מList.
אני לא פילוסוף גדול בOOP, אבל אמת שבחיים לא אעשה זאת, כנראה כי אף פעם אין בזה צורך.
בכל מקרה אצלנו גם אין שום תועלת בירושה הזו והיא אפילו מפריעה, כי המשתמש המתכנת יכול לטעות ולכתוב ככה:row.Add(123);
וזה יהיה חסר משמעות או גם יביא באגים, כי מספר העמודות הוא קבוע, וגם אם נאפשר לשנות אותו זה צריך להיות ברמת הטבלה לא ברמת שורה.
לכן צריך לעשות את הרשימה כשדה פרטי במחלקה ככה:public class Columns { private List<object> values = new(); public object this[int index] { get { return values[index]; } set { Console.WriteLine($"פה אתה יודע שהולך להשתנות תא. שם התא: '{columnsNames[index]}' ערך קודם: '{values[index] ?? "<ריק>"}' ערך חדש: '{value ?? "<ריק>"}'"); values[index] = value; } } string[] columnsNames; public Columns(string[] _columnsNames) { columnsNames = _columnsNames; foreach (var col in columnsNames) values.Add(null); } }
-
@dovid רציתי להגיב מזמן, ולא התאפשר...
ישנם שני חידושים בתשובה שלך,
חידוש אחד בתשובת הקוד הראשונה - אפשר להגיע לאינדקסר של המחלקה הקיימת למרות שיורשת מLIST שיש לו כבר אינדקסר בפנ''ע וללא פעולת OVERRIDE, ואיך לבצע זאת.
חידוש שני בתשובת הקוד השניה - אפשר ליצור אינדקסר חדש למחלקה בה אין לה מראש אינקסר, ואיך לבצע זאת.למרות החידוש השני הוא פשוט יותר, מבחינתי גדול החידוש הזה האחרון מן הראשון.
מה שעשיתי בפועל במסגרת האימון המצאת הגלגל (כבר אנימנסה ללמוד לעומק את הגלגל של C#, אל דאגה לא נתקעתי בזה... אבל אולי אחרי הלימוד כן אצור גלגל יפה יותר, ולכן אני משתף כאן) זה להעמיס כמה אינדסקרים - [int,int] לכל הנתונים לפי אינדקס, [int,string] לכל הנתונים לפי אינדקס שורה ושם עמודה, [Int] לשורה שלימה.
ומחקתי את קלאס CLOUMNS ויצרתי רק קלאס אחד 'values' פרייווט. וכו' (פונקציות להוספת ומחיקת שורה / עמודה).נתקעתי באיך אפשר ליצור TYPE לכל עמודה (מה שיחייב אותי לכאו' לקנן את השורות בעמודות ולא להיפך) אחרי שהיא מקוננת כבר בLIST כללי, וזה שצריך להגביל את בחירת הטיפוס רק לפי הטיפוסים של SQLSERVER.
גם איך להכניס את הנתונים לשאילתות => טיפוסים מסוג סטרינג להוסיף מרכאות, מסוג INT LONG DOUBLE וכו' לא להוסיף כלום, DATE וכו' שאני חושש לפספס משהו... -
@Y-Excel-Access כתב בC# - הוספת פעולת Poperty מאיבר לאיבר כשהם מקוננים ב List בתוך List.:
נתקעתי באיך אפשר ליצור TYPE לכל עמודה (מה שיחייב אותי לכאו' לקנן את השורות בעמודות ולא להיפך) אחרי שהיא מקוננת כבר בLIST כללי, וכשצריך להגביל את בחירת הטיפוס רק לפי הטיפוסים של SQLSERVER.
כל הרעיון של הטיפוסיות בשפת C# ובכל שפה עם טיפוסים, איננה לשביל לתקף נתונים (כלומר לוודא שהמשתמש הכניס ערך מספרי ולא טקסט) אלא לפיתוח נוקשה שמפחית שגיאות.
בשביל לכוף על נתונים להיות מספר ולא טקסט אין צורך (ובמקרה דינמי כמו פה גם אין יכולת) להשתמש בטיפוסיות של השפה. ניתן פשוט לשמור רשימה של סוגים ועל כל סוג את התיקוף המתאים וגם את ההטמעה המתאימה לו לפקודות הSQL, עם מרכאה בודדת וכדומה.גם איך להכניס את הנתונים לשאילתות => טיפוסים מסוג סטרינג להוסיף מרכאות, מסוג INT LONG DOUBLE וכו' לא להוסיף כלום, DATE וכו' שאני חושש לפספס משהו...
אתה אכן בטוח תפספס. בשביל הספורט אתה יכול לשחק, אבל באמת אסור לממש את זה לבד כי סיכוי גדול שמשאירים בעיה פתוחה. בדיוק בשביל זה לא ממציאים גלגל.
אגב, בספירה שהצעתי לך Dapper היא עושה את העבודה הזו, אבל צריך בכל זאת שהקלט יגיע עם טיפוס, ולכן חוזרת השאלה הקודמת שלך. אם תעשה רשימת סוגים אז תעשה פונקציה מתאימה לכל סוג שיודעת להמיר את הסוג הזה מטקסט, ואז זה יזוהה נכון ע"י הDapper. -
@dovid כתב בC# - הוספת פעולת Poperty מאיבר לאיבר כשהם מקוננים ב List בתוך List.:
בשביל לכוף על נתונים להיות מספר ולא טקסט אין צורך (ובמקרה דינמי כמו פה גם אין יכולת) להשתמש בטיפוסיות של השפה.
אגב, בספירה שהצעתי לך Dapper היא עושה את העבודה הזו, אבל צריך בכל זאת שהקלט יגיע עם טיפוס, ולכן חוזרת השאלה הקודמת שלך. אם תעשה רשימת סוגים אז תעשה פונקציה מתאימה לכל סוג שיודעת להמיר את הסוג הזה מטקסט, ואז זה יזוהה נכון ע"י הDapper.
איך? אתה מתכוון פשוט לבנות CASE? וכך לבדוק את הTYPE של כל ערך אם הוא נמצא ברשימה?
נשמע לי עבודה לא מתאימה לסדר של C#...מוזר שאין יכולת כזו של הגבלת טיפוסים, בTYPESCRIPT יש אפשרות להגבלת טיפוסים עם OR לדוגמה - string | int
אי אפשר להשתמש איכשהו עם <T> כמו שהאובייקט List לדוגמה משתמש?
אולי כן ליצור קלאס משנה עם קלט טיפוסיclass Column <T>
שיירש מאינטרפייס הכולל אישכשהו סוג מסויים של טיפוסים,
או רעיון דומה? -
@Y-Excel-Access אתה מפספס את העיקר.
המושג של T בליסט, וכל הטיפוסיות של C#, הינה תכנון מדוייק לזמן קמפול.
הגנריות של List שנתונת לבחור טיפוס כפרמטר לT, הינה דרך מהירה להשתמש בקלאס בהמון וריאציות כאילו כתבנו מחדש שוב ושוב את המחלקה, אבל זה לא נותן בכלל אפשרות לבחור בזמן ריצה איזה טיפוס יהיה שמה.במקרה שלך הטיפוס יוברר רק בעת ריצה, בזמן קמפול לא ידוע מאומה על מספר העמודות וסוגיהם ושמם. בשביל זה הטיפוסיות של Cֳ# בחיים לא תעזור.
מוזר שאין יכולת כזו של הגבלת טיפוסים, בTYPESCRIPT יש אפשרות להגבלת טיפוסים עם OR לדוגמה - string | int
מה זה בעצם
string | int
? זה משתנה שסובל שני טיפוסים. איך זה בדיוק יעזור לך? אתה צריך משתנה שסובל את כל הסוגים, בשביל זה יש את object או את dynamic.
מניסיון רב שנים בC# אני אומר לך שאין שום תועלת במשתנה רב טיפוסי, זה נועד נטו לדינמיות האמיתית של JS, ששמה זה בהווה הרבה יותר.איך? אתה מתכוון פשוט לבנות CASE? וכך לבדוק את הTYPE של כל ערך אם הוא נמצא ברשימה?
לא ממש הבנתי את השאלה, אולי בהזדמנות אכתוב קוד.
-
@dovid כתב בC# - הוספת פעולת Poperty מאיבר לאיבר כשהם מקוננים ב List בתוך List.:
הגנריות של List שנתונת לבחור טיפוס כפרמטר לT, הינה דרך מהירה להשתמש בקלאס בהמון וריאציות כאילו כתבנו מחדש שוב ושוב את המחלקה, אבל זה לא נותן בכלל אפשרות לבחור בזמן ריצה איזה טיפוס יהיה שמה.
במקרה שלך הטיפוס יוברר רק בעת ריצה, בזמן קמפול לא ידוע מאומה על מספר העמודות וסוגיהם ושמם. בשביל זה הטיפוסיות של Cֳ# בחיים לא תעזור.אתה צודק, לא חשבתי עד הסוף...
נפק"מ באמת היא רק במקרה ואתה יוצר טבלה חדשה מתוך הקוד. אבל בדר"כ אתה מנסה לקרוא לטבלה קיימת שאינה ממוקמת ב C# עצמו אלא חיצונית לו, בשונה מ ACCESS / SAP-ABAP שם לכאורה זה ממש חלק מהתוכנה...איך? אתה מתכוון פשוט לבנות CASE? וכך לבדוק את הTYPE של כל ערך אם הוא נמצא ברשימה?
לא ממש הבנתי את השאלה, אולי בהזדמנות אכתוב קוד.
יוצא שבאמת אין מנוס מלכתוב CASE על הטיפוס המתקבל (GetType), ולפ"ז להכניס לSQL בצורה הנכונה, (לא צריך לכתוב - אני יודע לכתוב CASE פשוט:)
כמובן כל זה יהיה נצרך כל עוד שאני לא משתמש בגלגל קיים: EntityFramework או ב Dapper או אפילו בתוך SqlClint בספריות DataTable-TableAdapter וכו' בתנאי שזה לא כולל הרכבת משפט SQL ידנית.