سؤال

وطيب، وكانت مشكلتي الفعلية هذا: كنت بتنفيذ IList<T>. عندما وصلت إلى CopyTo(Array array, int index)، وكان هذا حل بي:

void ICollection.CopyTo(Array array, int index)
{
    // Bounds checking, etc here.
    if (!(array.GetValue(0) is T))
        throw new ArgumentException("Cannot cast to this type of Array.");
    // Handle copying here.
}

وهذا يعمل في قانون بلدي الأصلي، والتي لا تزال تعمل. ولكن لديها عيب صغير، والتي لم تتعرض حتى بدأت الاختبارات بناء لذلك، وتحديدا هذا واحد:

public void CopyToObjectArray()
{
    ICollection coll = (ICollection)_list;
    string[] testArray = new string[6];

    coll.CopyTo(testArray, 2);
}

والآن، يجب أن يمر هذا الاختبار. فإنه يلقي ArgumentException عن عدم تمكنه من المدلى بها. لماذا ا؟ array[0] == null. الكلمة is دوما بإرجاع كاذبة عند فحص متغير التي تم تعيينها إلى null. الآن، وهذا هو مفيد لجميع أنواع من الأسباب، بما في ذلك تجنب dereferences فارغة، وما إلى ذلك ما جئت أخيرا مع لبلدي نوع التحقق كان هذا:

try
{
    T test = (T)array.GetValue(0);
}
catch (InvalidCastException ex)
{
    throw new ArgumentException("Cannot cast to this type of Array.", ex);
}

وهذه ليست أنيقة بالضبط، لكنه يعمل ... هل هناك طريقة أفضل على الرغم من؟

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

المحلول

والطريقة الوحيدة للتأكد هي مع التفكير، ولكن 90٪ من الوقت يمكنك تجنب تكلفة ذلك باستخدام array is T[]. معظم الناس تسير لتمرير مجموعة كتابتها بشكل صحيح، ولذا من شأنها أن تفعل. ولكن، يجب أن توفر دائما رمز للقيام الاختيار انعكاس كذلك، فقط في حالة. وهنا ما يبدو لي العامة المرجل لوحة مثل (ملاحظة: كتبت هذا هنا، من الذاكرة، وحتى هذا قد لا تجمع، ولكن يجب أن تعطي الفكرة الأساسية):

class MyCollection : ICollection<T> {
   void ICollection<T>.CopyTo(T[] array, int index) {
       // Bounds checking, etc here.
       CopyToImpl(array, index);
   }
   void ICollection.CopyTo(Array array, int index) {
       // Bounds checking, etc here.
       if (array is T[]) { // quick, avoids reflection, but only works if array is typed as exactly T[]
           CopyToImpl((T[])localArray, index);
       } else {
           Type elementType = array.GetType().GetElementType();
           if (!elementType.IsAssignableFrom(typeof(T)) && !typeof(T).IsAssignableFrom(elementType)) {
               throw new Exception();
           }
           CopyToImpl((object[])array, index);
       }
   }
   private void CopyToImpl(object[] array, int index) {
       // array will always have a valid type by this point, and the bounds will be checked
       // Handle the copying here
   }
}

تعديل : في طيب، نسيت أن أشير شيئا. A الإجابات زوجين استخدمت بسذاجة ما، في هذا القانون، تنص على ما element.IsAssignableFrom(typeof(T)) فقط. أنت <م> يجب أيضا السماح typeof(T).IsAssignableFrom(elementType)، كما يفعل بي سي إل، في حالة مطور يعلم أن كافة القيم في هذا ICollection محددة هي في الواقع من S نوع المستمدة من T، ويمر بمجموعة من نوع S[]

نصائح أخرى

وهناك أسلوب على نوع خصيصا لذلك، حاول:

if(!typeof(T).IsAssignableFrom(array.GetElementType()))

وList<T> يستخدم هذا:

try
{
    Array.Copy(this._items, 0, array, index, this.Count);
}
catch (ArrayTypeMismatchException)
{
  //throw exception...
}

وهنا هو اختبار القليل من حاول / catch مقابل التفكير:

object[] obj = new object[] { };
DateTime start = DateTime.Now;

for (int x = 0; x < 1000; x++)
{
    try
    {
        throw new Exception();
    }
    catch (Exception ex) { }
}
DateTime end = DateTime.Now;
Console.WriteLine("Try/Catch: " + (end - start).TotalSeconds.ToString());

start = DateTime.Now;

for (int x = 0; x < 1000; x++)
{
    bool assignable = typeof(int).IsAssignableFrom(obj.GetType().GetElementType());
}
end = DateTime.Now;
Console.WriteLine("IsAssignableFrom: " + (end - start).TotalSeconds.ToString());

والإخراج مما أدى إلى وضع الإصدار هو:

Try/Catch: 1.7501001
IsAssignableFrom: 0

في وضع التصحيح:

Try/Catch: 1.8171039
IsAssignableFrom: 0.0010001

والخلاصة، لا مجرد الاختيار التفكير. انه يستحق ذلك.

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