الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

الاضافات في سي#2 - Nullable Types

مغلق
بدأه معتز شمس في 29 سبتمبر 2005 · 13 رد · 2,328 مشاهدة · في Microsoft Visual C#.NET
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

شكرا أخي هاني على هذه المبادرات ... أنا أيضا كنت أحضر لكتابة الاضافات الجديده منذ زمن بعيد ولكن كنت أتكاسل كثيرا :D خصوصا بأن المنتدى لا يتفاعل بالشكل الذي يشجع.

لذلك ... بعد اذنك سأقوم بكتابة بعض الأشياء.

فأنا أود التحدث عن الـ <_< Nullable Types أولا ::

يمكننا أن نقوم في سي شارب 2 بكتابة كود بهذا الشكل ::

If ( myInteger == null )
{
	//
}

ولكن كل شئ لكنه قوانينه .. فالمتغير myInteger الذي قمت باختباره اذا كان null أم لا في الكود السابق لم أعلن عنه بالطريقه العاديه ولكن بطريقه جديده في سي شارب 2 ... قمت بالاعلان هكذا ::

Int? myInteger;

علامة الاستفهام الموجوده في الكود ليست خطأ مني ولكنها هى العلامه التي تستخدم عندما نريد الاعلان عن Value Type بأنه يمكن أن يسواي = null.

أو يمكن الاعلان عنه هكذا ::

Nullable<int> myInteger;

الأمر يشبه قليلا بشئ ما في قواعد البيانات .. فعندما نحدد الـ Date Type لاحدى الأعمده في جدول في قاعدة البيانات ... فاننا نقوم بتحديد مثلا نوعه int وبعد ذلك نخبر الجدول بأنه من الممكن أن لا تحتوي على شئ = Allow Nulls.

يمكننا التعامل مع هذه الأنواع من خلال الـ Properties مثل Value التي ترجع لنا القيمه التي يحتويها وأيضا من خلال HasValue التي ترجع لنا Boolean لتفيد لنا ما اذا كان المتغير يحتوي فعلا على قيمه أو لا يحتوي على شئ ( null ).

مثال ::

int? myInteger = null;

If(myInteger == null)
{
	//
}
myInteger  = 4;

int myInteger2 = myInteger.Value;

أو

int myInteger2 = (int)myInteger;

if (myInteger.HasValue)
{
	//True
}

ملحوظه :: الـ Value هى ReadOnly لذلك لا يمكنك كتابة ذلك ::

int? myInteger = 4;
myInteger.Value = 56;

طبعا تلك الأمثله السابقه لا تعبر عن الغرض الذي تم من أجله اضافه الـ Nullable Types في سي شارب 2.

قرأت في الـ MSDN ووجدتهم هناك يقولون أنها من اجل التعامل مع قواعد البيانات ::

avariable of nullable type can represent all the values of its underlying type, plus an additional null value. This is particularly useful when dealing with databases and other data types containing elements that may not be assigned a value, or which may not exist

وفعلا هذا الأمر جيد جدا كما توقعت ... فلن ينبغي علينا في سي شارب 2 التأكد من البيانات التي ترجع من قواعد البيانات من أنها null !

وهذا أنا علمته منذ زمن ولكني لم أجرب التعامل مع قواعد البيانات والاستفاده من الـ Nullable Types معها قبل ذلك اطلاقا , ولكن وأنا أكتب هذا الموضوع كنت قد قررت أن أضيف مثالا لها ولكني فشلت فشل كبير ,

انظر مثلا الى هذا الجدول الذي قمت بانشائه في SQL Server باسم ATMembers ::

29_09_05_04_49_11_1127994551tDesign.JPG

وقمت باضافة بعض البيانات ::

29_09_05_04_47_13_1127994433tData_.JPG

وبما أن الـ MSDN تقول أنه يمكننا استخدامها مع قواعد البيانات .. فبالتأكيد أنهم يقصدون كتابة الكود التالي باطمئنان في سي شارب 2 بدون اختبار اذا كانت القيمه تساوي DBNull أم لا ::

SqlCommand myCom = new SqlCommand("Select * from ATMembers", myCon);
SqlDataReader myReader = myCom.ExecuteReader();

int? theAge = null;

while (myReader.Read())
{
theAge = myReader.GetInt32(2);
//SomeMethod(theAge.Value,........
//.......
}

ولكن للأسف يظهر لي NullValueException مباشرة عندما يجد أن قيمة احد الـ السن = null !!! مع ان المفروض أن المتغير theAge هو Nullable أصلا !!

معنى هذا وكأنه لم يتم اضافة شيئا جديدا فمازال يجب علينا اختبار اذا كان الـ age هو DBNull أم لا أولا كما نفعل في السي شارب الحاليه , أيضا قمت بتجربة استخدام Methods أخرى موجوده في الـDataReader وبعض المحاولات الأخرى ولكن بنفس النتيجه.

بعد ذلك قررت أن أجرب الاضافه الى الجدول فربما تكون الـ Nullable Types مدعومه هناك ولكن أيضا فشلت ::

قمت بعمل SP اسمها AddMember للاضافه ::

CREATE Proc AddMember @fname char(10),@lname char(10),@age int
As
insert ATMembers values(@fname,@lname,@age)

وبعد ذلك جربت الكود ::

SqlCommand myCom = new SqlCommand("AddMember", myCon);
myCom.CommandType = CommandType.StoredProcedure;
 	 
string firstName = "Hany";
string lastName = "Atasy";
Nullable<int> age = null;
myCom.Parameters.AddWithValue("@fname", firstName);
myCom.Parameters.AddWithValue("@lname", lastName);
myCom.Parameters.AddWithValue("@age", age );
int rowsAffected = myCom.ExecuteNonQuery();

أيضا الكود لا يعمل ... طبعا هذا شئ غريب لا أفهمه حتى الآن .. ولكن من الواضح أن جميع الكلاسات المسؤوله عن التعامل مع قواعد البيانات لا تدعم الـ Nullable Types حتى الآن بالرغم من الـ MSDN يؤكدون فيه على أنها ميزه قويه عند التعامل مع قواعد البيانات ... فياريت لو يوجد من يعلم عن هذا الأمر .. يخبرنا به.

#Read

http://MoutazShams.blogspot.com

projects003.gif

أعتذر عن التغيب هذه الأيام نظرا للانشغال المريب

#2

إذا كان عندك (لا شيء) null، فطبعا لا تستطيع أن تستخدمه! وهذا سبب الأخطاء!!

فقيمة age تساوي لا شيء... وطبعا كيف تستخدمها؟!!

المسألة بإذن الله سهلة وهذا ما توفره لنا الأنواع اللاشيئية! Nullable Types!

int? age = null;
Console.Write(age.value); //Wrong لا تحتوي أصلا على قيمة!!!!!
if(age.hasValue) Console.Write(age.hasValue);
else Console.Write(0);

كما ترى هذا هو الاستخدام الطبيعي لأي شيء!! لكن بما أننا نستعمل أشياء بسيطة مثل الأرقام int double وغيرها! فبالمنطق إذا كانت قيمة لا شيء... فإننا سنعوضها مثلا بصفر!

فبدلا عن جملة (إذا) السابقة، نستعمل :

int? age= null;
Console.Write(age.getValueOrDefault());

وبهذا سترجع لنا قيمة سواء كانت null أو بها قيمة!

بالمناسبة أقترح وضع كل درس في موضوع مختلف، حتى إذا جاءت الأسئلة تكون منظمة حسب الدرس!

تم تعديل هذه المشاركة بواسطة أبومازن في 29 سبتمبر 2005 في 15:56

#3

نعم يا أخ أبو مازن ... فأنا معك تماما وما تفضلت بقوله هو الشئ الطبيعي فعند استخدام الـ Nullable Types يجب علينا ان نختبرها أولا , ولذلك قد أضافوا اليها HasValue لنتأكد بها اذا كانت الـ Nullable Types تحتوي على قيمه أم لا.

ولكن اذا قمنا باختبارها عن اذا ماكانت تحتوي على قيمه أم لا فهذا يعني أنها لم تقدم الينا أى ميزه عند التعامل مع قواعد البيانات.

مثلا اذا كان لدينا حقل int في جدول ويسمح بـ null فلا يمكننا وضع الـ Data به الموجوده في متغير من نوع<Nullable<int اذا كان هذا المتغير يحتوي على null ... وكذلك أيضا عند استرجاع القيمه من هذا الحقل لا يمكننا استرجاعها في متغير من نوع <Nullable<int اذا كان الحقل به null ! بالرغم من أن المتغير الذي من نوع <Nullable<int المفروض أنه يسمح بأن يحتوي على null ! واذا قمت بالاختبار هنا باستخدام IsDBNull قبل اسناد القيمه الموجوده في الحقل الى المتغير ... فلماذا اذا أستخدم الـ <Nullable<int كنوع للمتغير اذا كنت سأقوم بالاختبار أولا قبل اسناد القيم للمتغيرات ؟! فكان يمكنني أن استخدم int عادي !

اقتباس
بالمناسبة أقترح وضع كل درس في موضوع مختلف، حتى إذا جاءت الأسئلة تكون منظمة حسب الدرس!
يمكننا فعل ذلك بعد موافقة الأخ هاني .. لأن من الواضح أنه يمتلك خطه معينه.

#Read

http://MoutazShams.blogspot.com

projects003.gif

أعتذر عن التغيب هذه الأيام نظرا للانشغال المريب

#4

أعتقد أني سمعت في ال MDC السابق أن الNullable Type تمكنك أن تضع قيمة افتراضية داخل المتغير في حالة أن قيمته تكون Null بمعنى أنه لو كان Nullable<int> فانه مثلاً سيوضع القيمة 0 في المتغير في حالة الNull و هذه الطريقة تجعلك لا تحتاج لعمل check اذا كان الNullabl variable يحتوي null أم لا

Sr. Software Development Engineer
Hulu, LLC
My Blogs

#5

عندما قرأت مشاركتك مره أخرى وجدت شيئا آخر وهو سبب دهشتك ::

اقتباس
فقيمة age تساوي لا شيء... وطبعا كيف تستخدمها؟!!

وانت تقصد حين استخدمتها في الكود ::

Nullable<int> age = null;
myCom.Parameters.AddWithValue("@age", age );
int rowsAffected = myCom.ExecuteNonQuery();

طبعا لم أكن أستطيع كتابة الكود بدون ما أكتب ::

Nullable<int> age = null;

فلو كتبنا ::

Nullable<int> age;

فلن يقوم الـ Compiler بعمل Compile لأن age UnAssigned.

واذا كانت تلك هى المشكله , فهذا كان بالنسبه للاضافه. فلماذا تظهر مشكله عند استرجاع قيمة من المحتمل أنها تكون null من قاعدة البيانات داخل Nullable Type ؟

اقتباس
int? age= null;
Console.Write(age.getValueOrDefault());

وبهذا سترجع لنا قيمة سواء كانت null أو بها قيمة!

جربتها ولكن لا أمل :) لو كان بها null لن ترجع null فسوف ترجع صفر !

أنا عند الاضافه قمت باستخدام GetValueOrDefault

myCom.Parameters.AddWithValue("@age", age.GetValueOrDefault());

ولكنه قام باضافة 0 الى الـ age في الجدول وليس null !

اقتباس
أعتقد أني سمعت في ال MDC السابق أن الNullable Type تمكنك أن تضع قيمة افتراضية داخل المتغير في حالة أن قيمته تكون Null بمعنى أنه لو كان Nullable<int> فانه مثلاً سيوضع القيمة 0 في المتغير في حالة الNull و هذه الطريقة تجعلك لا تحتاج لعمل check اذا كان الNullabl variable يحتوي null أم لا
أعتقد أنك تقصد الشكل الآخر لـ GetValueOrDefault فيمكننا اسناد قيمه افتراضيه للمتغير اذا كان المتغير الـ Nullable لا يساوي شئ ::

int? myinteger = 55;
int myX = myinteger.GetValueOrDefault(44);
// myX now = 55

myinteger = null;
myX = myinteger.GetValueOrDefault(44);
// myX now = 44

ولكن الـ DefaultValue يجب أن يكون من نفس النوع ( الـ T ) ففي الكود السابق .. يجب أن يكون من نوع int ... ولكننا نريد طريقه لنجعله يساوي null لنضعه في حقل ما بيـ Allow Nulls.

#Read

http://MoutazShams.blogspot.com

projects003.gif

أعتذر عن التغيب هذه الأيام نظرا للانشغال المريب

#6

أرجو أن لا نخرج عن الموضوع بمقارنة تفصيلية بين سي# والجافا.

بعض المصطلحات لا أفهمها عند ترجمتها لذلك أفضل أن يتم وضع المصطلح الأساسي باللغة الانجليزية إذا تمت ترجمته.

بالنسبة للموضوع .. فليس لدي مانع أن يتم تقسيمة إلى عدة مواضيع كل موضوع يتكلم عن اضافة جديدة في سي#2 .. ولكني أفضل أكثر أن يتم نقل المواضيع إلى قسم خاص في الصفحة الرئيسية أو محل آخر من أجل زيادة الفائدة العامة ، وممكن هذا طبعا بعد أن ننتهي من شرح الاضافات . (إذا أمكن نقل المواضيع الخاصة في Nullable type إلى موضوع آخر اسمه "الاضافات في سي#2 - Nullable Types" أكون لكم من الشاكرين . أيضا تغيير اسم هذا الموضوع إلى "الاضافات في سي#2 - Generics")

شكرا أخ معتز على مشاركتك . سوف أشرح هنا بعض النقاط التي غبت عن شرحها:

----------------------

تمت اضافة عامل جديد يسمى ب Null-Coalescing operator (؟؟)

- يستخدم هذا العامل من أجل اسناد قيمة افتراضية للمتحول إذا كانت القيمة عبارة عن null

- يعمل تماما مثل التابع GetValueOrDefault

مثال:

            int? test = null;
            int x = test ?? 5;  // 5 is the default value if test is null
            int x = test.HasValue ? test.Value : 5;     // same as the one before
            int x = test.GetValueOrDefault(5);  // Same as the one before

            string fileName = null;
            string title = fileName ?? "Untitled";  // title will be "Untitled" if fileName is null

بالنسبة لموضوع الdatabase فالقيمة التي يتم ارجاعها هي System.DBNull إذا كان الحقل في قاعدة البيانات عبارة عن Null وهذه القيمة لا يمكن تحويلها مباشرة إلى Nullable type لذلك يجب عليك أن تكتب كود خاص من أجل نقل القيمة من قاعدة البيانات إلى قيمة في سي# . بالنسبة للكود الذي وضعته في مثالك الذي نتج عنه exception فانه بسبب أن ال Strong Typed Dataset في سي# لا تدعم بعد ال Nullable Type .

        static int? Get(int column, SqlDataReader reader)
        {
            object value = reader.GetValue(column);
            return value == DBNull.Value ? null : (int?)value;
        }

Coding on the Cloud and for the Cloud!

My Blog

#7

اشكرك معتز على هذا الدرس الروعه !

مش بعيد عليك هذا الابداع

abo_alzobaer@hotmail.com

#8

العفو يا سيلفاتور وشكرا على اضافاتك يا أخي هاني ونتمنى أن يتم دعم الـ Nullable Types عند التعامل مع البيانات في الاصدار النهائي.

#Read

http://MoutazShams.blogspot.com

projects003.gif

أعتذر عن التغيب هذه الأيام نظرا للانشغال المريب

#9

بالنسبة لVisual Studio فهي في المرحلة الأخيرة لذلك ليس بالامكان على حسب اعتقادي اضافة أي ميزات جديدة لها. وأعتقد السبب الآخر هو أنه من الصعب تعديل ال Strong typed dataset الحالية لجعلها تدعم ال Nullable types وذلك بسبب التوافقة مع الاصدارات السابقة في .net

ولكن الأمر الجيد هو اني لاحظت أن ال TableAdapter يدعم ال Nullable types . والTableAdapter قد تمت اضافته جديدا في .net وهو يعطي ميزات جديدة عن ال DataAdapter من بينها أنه يمكن انشاء أكثر من تابع فيه لتنفيذ query معينة بحيث يكون خرج هذه ال query كلها من نفس نوع الجدول . مثلا إذا كان لدينا جدول اسمه Student فيمكن انشاء توابع مثل GetAll و GetByName و GetById و هكذا ...

الTableAdapter يتم انشائه بشكل آلي عن طريق ال Database designer في Visual Studio . والكود التالي يوضح جزء من هذا الكود المنتج على جدول اسمه Number ، لاحظ فيه استخدام ال Nullable type

  public virtual int FillBy(Database1DataSet.NumberDataTable dataTable, System.Nullable<int> number) {
      this.Adapter.SelectCommand = this.CommandCollection[1];
      if ((number.HasValue == true)) {
          this.Adapter.SelectCommand.Parameters[0].Value = ((int)(number.Value));
      }
      else {
          this.Adapter.SelectCommand.Parameters[0].Value = System.DBNull.Value;
      }
      if ((this.ClearBeforeFill == true)) {
          dataTable.Clear();
      }
      int returnValue = this.Adapter.Fill(dataTable);
      return returnValue;
  }

Coding on the Cloud and for the Cloud!

My Blog

#10

شكرا يا أخ هاني على تفاعلك.

بالنسبه للـ TableAdapter أنا أول مره أسمع عنه :) جزاك الله خيرا.

قرأت عنه في الـ MSDN ولكنني لم أستوعب كيفية دعمه للـ Nullable Types ؟ فاذا كانوا يقصدون أنه بكتابة هذا الكود الذي تفضلت بكتابته ::

if ((number.HasValue == true)) {
this.Adapter.SelectCommand.Parameters[0].Value = ((int)(number.Value));
}
else {
      this.Adapter.SelectCommand.Parameters[0].Value =  System.DBNull.Value;
}

أنه بذلك يدعم الـ Nullable Types فأعتقد أنهم من المفروض يجعلون فائدة الـ Nullable Types أسمى من ذلك أثناء التعامل بها مع قواعد البيانات أو لا يقولون ما قالوه في الـ MSDN أن الـ Nullable Types رائعه في التعامل مع قواعد البيانات.

أعتقد أنهم قاموا بجعل الـ TableAdapters يمكن أن يتم انشائها أوتوماتيكيا ( طبعا هذا أمر رائع ) حتى لا يثور المطورين على الـ Nullable Types مع قواعد البيانات !!!

أنا أرى أنه ينبغي عمل نوع Null جديد بجانب الـ DBNull يمكننا اسناده للـ Nullable Types مثلا ::

Nullable<int> myInteger = NullableTypes.Null;

ويكون هذا الـ NullableTypes.Null الجديد بامكاننا تحويله الى الـ DBNull بطريقه Implicity ... وبذلك لن نختبر المتغيرات الـ Nullable اذا كانت تحتوي على قيمه أم لا من أجل أنها اذا كانت لا تحتوي على قيمه نقوم باضافة DBNull !!!!!!

أى من الآخر ... أتمنى كتابة الكود الذي تفضلت به يا أخ هاني هكذا مثلا ::

public virtual int FillBy(Database1DataSet.NumberDataTable dataTable, System.Nullable<int> number) 
{
// ............
this.Adapter.SelectCommand = this.CommandCollection[1];

this.Adapter.SelectCommand.Prameters[0]
.Value = number.GetValueOrDefalut(NullableTypes.Null);
// .............
}

أى بطريقه Implicity جدا يقوم باضافة قيمة الـ number الى الجدول .. ولو كان number لا يحتوي على قيمه يقوم باضافة null وليس صفر الى الحقل عن طريق تحويل NullableTypes.Null الى DBNull.

تم تعديل هذه المشاركة بواسطة معتز شمس في 2 أكتوبر 2005 في 10:07

#Read

http://MoutazShams.blogspot.com

projects003.gif

أعتذر عن التغيب هذه الأيام نظرا للانشغال المريب

#11

سلام أخ معتز ..

لم أفهم جيدا طريقتك هل بالامكان وضع كود كامل لها ؟

TableDataAdabter هي فقط حلقة وصل بين ال DataTable العادية والDatabase .. الدعم هنا فقط عبارة عن امكانية تمرير Nullable types للqueries التي قمت بعملها . طبعا هذا الدعم كافي ولا يمكن أن يتم دعمها اكثر من هذا.

لدي فكرة وهي امكانية استخدام partial types من أجل كتابة كود خاص بك يقوم باضافة توابع على الDataset clases التي قام بانشائها فجوال ستديو آليا . وطبعا هذا ممكن لأن الكلاسات التي ينشئها فجوال ستديو تكون من نوع partial . الكود التالي قمت بكتابته لاضافة property جديدة للكلاس SchoolDataSet.ClassromRow :

    partial class SchoolDataSet
    {
        public partial class ClassroomRow
        {
            public int? whiteboardsNullable
            {
                get
                {
                    object value = this[this.tableClassroom.whiteboardsColumn];
                    return (value == System.DBNull.Value) ? null : (int?)value;
                }
                set
                {
                    if (value.HasValue)
                        this[this.tableClassroom.whiteboardsColumn] = value.Value;
                    else
                        this[this.tableClassroom.whiteboardsColumn] = System.DBNull.Value;
                }
            }
        }
    }

الكلاس الأساسي SchooldataSet تم انشائه عن طريق فجوال ستديووهو يضم جميع الtables في قاعدة البيانات School . داخل هذه ال Dataset تم انشاء الجدول ClassroomTable و ClassroomRow التي تمثل سطر في الجدول السابق.

المثال السباق تم وضعه في ملف cs منفصل داخل نفس المشروع . و باستخدام partial فاننا نخبر المترجم أن هذا الكلاس هو تتمة للكلاس الأساسي . ClassroomRow الأساسية تحتوي على whiteboards ولكنها من نوع int .. ولكن الآن يمكنك استخدام whiteboardsNullable وهي من نوع int? ..

Coding on the Cloud and for the Cloud!

My Blog

#12
اقتباس
لم أفهم جيدا طريقتك هل بالامكان وضع كود كامل لها ؟
لا تشغل بالك بها كثيرا فانها مجرد تخيل أو حلم من أحلام اليقظه :)

الفكره التي كتبتها فكره جيده ... أنا فقط كل ما يزعجني في الـ Nullable Types يتضح في الكود الذي كتبته في مشاركتك خصوصا الكود الموجود داخل set ::

if (value.HasValue)

// theField = value.Value

else

// theField = System.DBNull.Value

فأنا عندما سمعت عن الـ Nullable Types ومميزاتها العظيمه التي ذكروها عند التعامل مع قواعد البيانات .. تخيلت أنني سأكتب الكود بعد ذلك هكذا مباشرة ::

theField = value.Value;

وسيتم اضافة القيمه الموجوده سواء كانت قيمه أو كانت null.

#Read

http://MoutazShams.blogspot.com

projects003.gif

أعتذر عن التغيب هذه الأيام نظرا للانشغال المريب

#13

فعلا انا كنت أحب أيضا أن أرى هذه الميزة ترى طريقها إلى الداتابيس بشكل كامل . ولكن على ما أظن سوف يتم تحسينها في الاصدارات القادمة من دوت نيت .

لو راجعت هذه الرابطة

http://blogs.msdn.com/vbteam/archive/2005/01/12/351495.aspx

لترى بالظبط لماذا لم يستطع الفريق في ميكروسوفت من اضافة هذه الميزة في strong typed datasets .

Coding on the Cloud and for the Cloud!

My Blog

#14

هل ممكن من المشرفين تثبيت هذا الموضوع أو نسخه إلى الصفحة الرئيسية في الموقع ؟

Coding on the Cloud and for the Cloud!

My Blog

هذا الموضوع مغلق.

مواضيع مشابهة