هل الطرق التي تعدل معلمات نوع المرجع سيئة؟

StackOverflow https://stackoverflow.com/questions/621414

  •  05-07-2019
  •  | 
  •  

سؤال

لقد رأيت طرقًا كهذه:

public void Foo(List<string> list)
{
    list.Add("Bar");
}

هل هذه الممارسة الجيدة لتعديل المعلمات بطريقة ما؟

ألن يكون هذا أفضل؟

public List<string> Foo(List<string> list)
{
    // Edit
    List<string> newlist = new List<string>(list);
    newlist.Add("Bar");
    return newlist;
}

يبدو الأمر وكأنه المثال الأول له آثار جانبية غير متوقعة.

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

المحلول

في المثال الذي قدمته ، يبدو الأول أجمل بكثير من الثانية. إذا رأيت طريقة قبلت قائمة وأعادت أيضًا قائمة ، فإن افتراضي الأول هو أنه كان يعيد قائمة جديدة وعدم لمس تلك التي أعطيت. الطريقة الثانية ، لذلك ، هي واحدة ذات آثار جانبية غير متوقعة.

طالما تم تسمية أساليبك بشكل مناسب ، فهناك خطر ضئيل في تعديل المعلمة. النظر في هذا:

public void Fill<T>(IList<T> list)
{
    // add a bunch of items to list
}

مع اسم مثل "Fill" ، يمكنك أن تكون متأكدًا تمامًا من أن الطريقة ستعدل القائمة.

نصائح أخرى

بصراحة ، في هذه الحالة ، تقوم كلتا الطريقتين بنفس الشيء أو أقل. كلاهما سيعدل List تم تمرير ذلك.

إذا كان الهدف هو أن يكون له قوائم غير قابلة للتغيير بمثل هذه الطريقة ، فيجب أن يقوم المثال الثاني بإعداد نسخة من List تم إرساله ، ثم يؤدي Add العملية على الجديد List ثم أعد ذلك.

لست على دراية بـ C# ولا .NET ، لذلك سيكون تخميني شيئًا على طول خط:

public List<string> Foo(List<string> list)
{
    List<string> newList = (List<string>)list.Clone();
    newList.Add("Bar");
    return newList;
}

بهذه الطريقة ، الطريقة التي تستدعي Foo سيتم الحصول على الطريقة التي تم إنشاؤها حديثًا List عاد ، والأصل List لم يتم تمرير ذلك.

هذا الأمر متروك حقًا لـ "عقد" مواصفاتك أو واجهة برمجة التطبيقات ، لذلك في الحالات التي يكون فيها Listيمكن تعديل S فقط ، لا أرى مشكلة في الذهاب مع النهج الأول.

أنت تفعل الشيء نفسه بالضبط في كلتا الطريقتين ، فقط واحد منهم هو إعادة القائمة نفسها.

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

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

public int GetLongest(IEnumerable<string> list) {
    int len = 0;
    foreach (string s in list) {
        len = Math.Max(len, s.Length);
    }
    return len;
}

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

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

لقد جعل ظهور أساليب التمديد من الأسهل بعض الشيء التعامل مع الطرق التي تقدم الآثار الجانبية. على سبيل المثال ، في مثالك يصبح أكثر سهولة في القول

public static class Extensions
{
  public static void AddBar(this List<string> list)
  {
     list.Add("Bar");
  }
}

واتصل بها مع

mylist.AddBar();

مما يجعل من الواضح أن هناك شيئًا ما يحدث في القائمة.

كما هو مذكور في التعليقات ، يعد هذا مفيدًا للغاية في القوائم لأن التعديلات على القائمة يمكن أن تكون أكثر إرباكًا. على كائن بسيط ، أود أن أميل إلى تعديل الكائن في مكانه.

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