سؤال

لقد قمت بإنشاء الخاصية التالية، التي ألقيت InvalidCastException إذا تم الوصول إلى Getter عندما ViewState[TOTAL_RECORD_COUNT] كان null.

public long TotalRecordCount
{
    get { return (long)(ViewState[TOTAL_RECORD_COUNT] ?? -1); }
    set { ViewState[TOTAL_RECORD_COUNT] = value; }
}

فكرتي هي أنه حاول Unbox بشكل غير صحيح ViewState[TOTAL_RECORD_COUNT] إلى int, ، الذي فشل لأنه يحتوي على long, ، لكنني أعتقد أنه قد يكون هناك عيب في هذا المنطق. سأترك الأمر كممارسة تمرين للقارئ للإشارة إلى هذا العيوب.

لقد غيرت منذ ذلك الحين أن الممتلكات لقراءة

public long TotalRecordCount
{
    get { return (long?)ViewState[TOTAL_RECORD_COUNT] ?? -1; }
    set { ViewState[TOTAL_RECORD_COUNT] = value; }
}

الذي يعمل فقط تضخم. ومع ذلك، فإنني أتساءل ما هو الخطأ في نسختي الأصلية ... Stackoverflow إلى الإنقاذ؟

لاحظ أنه إذا حاولت التنفيذ (long)(ViewState[TOTAL_RECORD_COUNT] ?? -1) في النافذة الفورية، أحصل على رسالة الخطأ Cannot unbox 'ViewState[TOTAL_RECORD_COUNT] ?? -1' as a 'long' وإذا قمت بالتنفيذ (ViewState[TOTAL_RECORD_COUNT] ?? -1).GetType().Name انا حصلت Int32. وبعد يمكنني التنفيذ (long)-1 وينتهي مع -1 ك Int64...اذا ما الأمر؟

هل كانت مفيدة؟

المحلول

نوع العودة من ViewState المفهرس هو Object (أفترض أنك تعني ASP.NET ViewState هنا). الآن النظر في ما يجب على المحول البرمجي القيام به عندما يرى هذا (أي ما يعادل الكود الخاص بك):

object o = ViewState[...];
var x = o ?? -1;

يجب أن يستنتج النتيجة نوع التعبير o ?? -1 بطريقة ما. على اليسار يرى object, ، على اليمين هو int. وبعد من الواضح أن النوع الأكثر عمليا لهذا التعبير هو أيضا object. وبعد ومع ذلك، هذا يعني أنه إذا انتهى فعلا باستخدام ذلك -1 (لان o كان فارغا)، سيكون عليه تحويله إلى object - ول int, وهذا يعني الملاكمة.

لذا x هو من النوع object, ، وقد يحتوي على int (وربما أيضا بعض أنواع غير متكاملة أخرى - لا نعرف ما هو في العارض الخاص بك، يمكن أن يكون short, ، علي سبيل المثال). الآن تكتب:

long y = (long)x;

حيث x هو object, هذا هو Unboxing. ومع ذلك، يمكنك فقط أنواع قيمة Unbox فقط في نفس النوع بالضبط (مع الاستثناء الوحيد الذي يمكنك استبدال نوع موقع للحصول على نوع غير موقعي مكافئ، وتناض على نوع الأساس الأساسي). وهذا هو، لا يمكنك unbox int داخل long. وبعد طريقة أبسط بعيدة لتحديث هذا، مع عدم وجود رمز "إضافي"، سيكون:

object x = 123;
long y = (long)x;

التي يرمي أيضا InvalidCastException, ، وبالضبط نفس السبب.

نصائح أخرى

المصبوب يجب أن يكون خطوة واحدة فقط.

التعبير <object> ?? <int> سوف تنتج كائن آخر، وعندما تكون القيمة الأولى فارغة، أي. ViewState[TOTAL_RECORD_COUNT] لاغيا، ثم تكون القيمة الناتجة كائن، مع INT32 محاصر فيه.

نظرا لأنك لا تستطيع Unbox كائن يحتوي على INT32 لفترة طويلة، فأنت بحاجة إلى أول صندوق Unbox إلى INT32، ثم يلقيها لفترة طويلة.

المشاكل ليست بصورة غير مرعبة ViewState[TOTAL_RECORD_COUNT], ، المشكلة هي الملاكمة والعلوك من -1.

   ViewState[TOTAL_RECORD_COUNT] ?? -1

أنت تستخدم؟ المشغل على "كائن" و "int". النوع الناتج هو "كائن". هذا يعني أن -1 ستكون محاصر (كما int) عند عدم وجود الحقل في حالة العرض.

ثم تعطل البرنامج في وقت لاحق عندما يحاول Unbox (int) -1 منذ فترة طويلة.

في الأصل، إذا اخترته، كنت تفعل:

(ViewState[TOTAL_RECORD_COUNT] ?? -1)

ال المشغل الفارق (؟) تم تصميمه خصيصا إلى:

لتحديد القيمة الافتراضية ل أنواع القيمة المطلقة إلى جانب أنواع المراجع.

في حالتك، يمكنك استخدامه للتعامل مع نظام .Object، لذلك سيستغرق الأمر "-1"، معاملة ذلك كنسخة INT32، وصندوقها في نظام جديد. بعد ذلك، يحاول Unbox Int32 منذ فترة طويلة، والتي فشلت، نظرا لأن المصبوب لا يمكن ل Unbox وتغيير النوع في خطوة واحدة.

يمكنك حل هذا بسهولة من خلال تحديد أن الخاص بك -1 طويل باستخدام لاحقة L:

public long TotalRecordCount
{
    get { return (long)(ViewState[TOTAL_RECORD_COUNT] ?? -1L); }
    set { ViewState[TOTAL_RECORD_COUNT] = value; }
}

Int64. هو نوع القيمة، لذلك الصب null لنوع القيمة سوف يرمي دائما استثناء (NullReferenceException). وسنجح الصب إلى Int32 إلى Int64 ولن ترمي InvalidCastException.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top