سؤال

لدي قائمة بالبنيات وأريد تغيير عنصر واحد.على سبيل المثال :

MyList.Add(new MyStruct("john");
MyList.Add(new MyStruct("peter");

الآن أريد تغيير عنصر واحد:

MyList[1].Name = "bob"

ومع ذلك، كلما حاولت القيام بذلك، أحصل على الخطأ التالي:

لا يمكن تعديل قيمة إرجاع system.collections.generic.list.itis [int] 'لأنها ليست متغير

إذا استخدمت قائمة الفئات، لا تحدث المشكلة.

أعتقد أن الإجابة تتعلق بالبنيات باعتبارها نوعًا من القيمة.

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

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

المحلول

MyList[1] = new MyStruct("bob");

يجب دائمًا تصميم البنيات في C# لتكون غير قابلة للتغيير (أي ليس لديها طريقة لتغيير حالتها الداخلية بمجرد إنشائها).

في حالتك، ما تريد القيام به هو استبدال البنية بأكملها في فهرس المصفوفة المحدد، وليس محاولة تغيير خاصية أو حقل واحد فقط.

نصائح أخرى

ليس تماما.لا ينبغي أن يكون تصميم النوع كفئة أو بنية مدفوعًا بحاجتك إلى تخزينه في مجموعات :) يجب أن تنظر إلى "الدلالات" المطلوبة

المشكلة التي تراها ترجع إلى دلالات نوع القيمة.كل متغير/مرجع لنوع القيمة هو مثيل جديد.عندما تقول

Struct obItem = MyList[1];

ما يحدث هو أنه يتم إنشاء مثيل جديد للبنية ويتم نسخ جميع الأعضاء واحدًا تلو الآخر.بحيث يكون لديك نسخة من MyList[1] أي.2 حالات.الآن إذا قمت بتعديل obItem، فلن يؤثر ذلك على الأصل.

obItem.Name = "Gishu";  // MyList[1].Name still remains "peter"

تحمل معي الآن لمدة دقيقتين هنا (يستغرق هذا بعض الوقت حتى تبتلع..لقد فعلت ذلك بالنسبة لي :) إذا كنت بحاجة حقًا إلى تخزين هياكل في مجموعة وتعديلها كما هو موضح في سؤالك ، فسيتعين عليك جعل بنيتك تعرض واجهة (لكن هذا سيؤدي إلى الملاكمة).يمكنك بعد ذلك تعديل البنية الفعلية عبر مرجع الواجهة، الذي يشير إلى الكائن المعبأ.

يوضح مقتطف الكود التالي ما قلته للتو أعلاه

public interface IMyStructModifier
{
    String Name { set; }
}
public struct MyStruct : IMyStructModifier ...

List<Object> obList = new List<object>();
obList.Add(new MyStruct("ABC"));
obList.Add(new MyStruct("DEF"));

MyStruct temp = (MyStruct)obList[1];
temp.Name = "Gishu";
foreach (MyStruct s in obList) // => "ABC", "DEF"
{
    Console.WriteLine(s.Name);
}

IMyStructModifier temp2 = obList[1] as IMyStructModifier;
temp2.Name = "Now Gishu";
foreach (MyStruct s in obList) // => "ABC", "Now Gishu"
{
    Console.WriteLine(s.Name);
}

هث.سؤال جيد.
تحديث: @Hath - لقد جعلتني أركض للتحقق مما إذا كنت قد تجاهلت شيئًا بهذه البساطة.(سيكون الأمر غير متسق إذا لم تكن خصائص أداة الضبط والطرق كذلك - فعالم .Net لا يزال متوازنًا :)
طريقة الضبط لا تعمل
تقوم obList2[1] بإرجاع نسخة سيتم تعديل حالتها.البنية الأصلية في القائمة تبقى بدون تعديل.لذا يبدو أن Set-via-Interface هي الطريقة الوحيدة للقيام بذلك.

List<MyStruct> obList2 = new List<MyStruct>();
obList2.Add(new MyStruct("ABC"));
obList2.Add(new MyStruct("DEF"));
obList2[1].SetName("WTH");
foreach (MyStruct s in obList2) // => "ABC", "DEF"
{
    Console.WriteLine(s.Name);
}

ليس الأمر أن الهياكل "غير قابلة للتغيير".

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

كما يقول أندرو، عليك استبدال البنية بأكملها.على الرغم من أنني أعتقد أن عليك أن تسأل نفسك عن سبب استخدامك للبنية في المقام الأول (بدلاً من الفصل الدراسي).تأكد من أنك لا تفعل ذلك بسبب مخاوف التحسين المبكرة.

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

لسوء الحظ، كما لاحظت، فإن المجموعات المضمنة في .net ضعيفة حقًا في الكشف عن كائنات القيمة الموجودة فيه.أفضل رهان لك عادةً هو القيام بشيء مثل:

  MyStruct temp = myList[1];
  temp.Name = "Albert";
  myList[1] = temp;

مزعج إلى حد ما، وليس على الإطلاق Threadsafe.لا يزال هناك تحسين على قائمة نوع الفصل، حيث قد يتطلب القيام بنفس الشيء:

  myList[1].Name = "Albert";

ولكن قد يتطلب الأمر أيضًا ما يلي:

  myList[1] = myList[1].Withname("Albert");

أو ربما

  myClass temp = (myClass)myList[1].Clone();
  temp.Name = "Albert";
  myList[1] = temp;

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

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