سؤال

بعد المناقشات هنا على SO، قرأت بالفعل عدة مرات الملاحظة التي مفادها أن الهياكل القابلة للتغيير هي "شريرة" (كما هو الحال في الإجابة على هذا سؤال).

ما هي المشكلة الفعلية مع قابلية التغيير والبنيات في C#؟

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

المحلول

والبنيات هي أنواع القيمة وهو ما يعني نسخها عندما يتم تمرير حولها.

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

إذا البنية الخاص بك هو غير قابل للتغيير ثم جميع النسخ التلقائي الناتجة عن يتم تمريرها من حيث القيمة سوف تكون هي نفسها.

إذا كنت تريد تغييره ما عليك القيام به بوعي من خلال إنشاء مثيل جديد من البنية مع البيانات المعدلة. (وليس نسخة)

نصائح أخرى

وأين تبدأ ؛-p

اريك ليبرت بلوق و عبارة دائما جيدة للتعبير:

<اقتباس فقرة>   

وهذا هو سبب آخر لماذا قابلة للتغيير   أنواع قيمة شريرة. حاول دائما   جعل أنواع قيمة ثابتة.

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

Foo foo = list[0];
foo.Name = "abc";

وماذا فعلت ذلك التغيير؟ أي شيء مفيد ...

ونفس الشيء مع خصائص:

myObj.SomeProperty.Size = 22; // the compiler spots this one

ويجبرك على القيام به:

Bar bar = myObj.SomeProperty;
bar.Size = 22;
myObj.SomeProperty = bar;

وأقل خطيرة، هناك مسألة حجم. كائنات قابلة للتغيير على تميل لديك خصائص متعددة؛ بعد ما اذا كان لديك البنية مع اثنين من ints، وstring، وDateTime وbool، يمكنك حرق بسرعة كبيرة من خلال الكثير من الذاكرة. مع فئة، يمكن للمتصلين متعددة تشترك في إشارة إلى نفس المثيل (إشارات صغيرة).

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

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

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

والبنيات قابلة للتغيير ليست شريرة.

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

لا يمكنني ان اصفه استخدام البنية الثابتة في هذه الحالات استخدام صالحة تماما "الشر".

وأستطيع أن أرى النقطة التي جملة C # الصورة لا يساعد على التمييز وصول عضوا في نوع قيمة أو نوع مرجع، لذلك أنا كل ل<م> التفضيل البنيات الثابتة، التي فرض ثبات ، على البنيات قابلة للتغيير.

ولكن، بدلا من مجرد وصفها البنيات الثابتة بأنها "الشر"، وأنصح لتبني لغة والدعوة القاعدة أكثر مفيدة وبناءة من الابهام.

وعلى سبيل المثال: <م> ". البنيات وأنواع القيمة والتي يتم نسخها بشكل افتراضي تحتاج مرجع إذا كنت لا ترغب في نسخها" أو "محاولة للعمل مع البنيات للقراءة فقط أولا" .

الهياكل ذات الحقول أو الخصائص العامة القابلة للتغيير ليست شريرة.

الأساليب الهيكلية (تتميز عن محددات الخاصية) التي تحول "هذا" هي شريرة إلى حد ما، فقط لأن .net لا يوفر وسيلة لتمييزها عن الأساليب التي لا تفعل ذلك.يجب أن تكون الأساليب الهيكلية التي لا تغير "هذا" قابلة للاستدعاء حتى في بنيات القراءة فقط دون الحاجة إلى النسخ الدفاعي.لا ينبغي أن تكون الأساليب التي تقوم بتغيير "هذا" قابلة للاستدعاء على الإطلاق في بنيات القراءة فقط.نظرًا لأن .net لا يريد منع استدعاء أساليب البنية التي لا تعدل "هذا" على بنيات للقراءة فقط، ولكنه لا يريد السماح بتغيير بنيات للقراءة فقط، فإنه ينسخ بنيات للقراءة بشكل دفاعي. السياقات فقط، ويمكن القول إنها تحصل على أسوأ ما في العالمين.

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

struct PointyStruct {public int x,y,z;};
class PointyClass {public int x,y,z;};

void Method1(PointyStruct foo);
void Method2(ref PointyStruct foo);
void Method3(PointyClass foo);

لكل طريقة أجب عن الأسئلة التالية:

  1. بافتراض أن الطريقة لا تستخدم أي تعليمات برمجية "غير آمنة"، فهل يمكنها تعديل foo؟
  2. إذا لم يكن هناك أي مراجع خارجية لـ "foo" قبل استدعاء الطريقة، فهل يمكن وجود مرجع خارجي بعد ذلك؟

الإجابات:

السؤال رقم 1:
Method1():لا (القصد واضح)
Method2():نعم (القصد واضح)
Method3():نعم (نية غير مؤكدة)
السؤال 2:
Method1():لا
Method2():لا (ما لم تكن آمنة)
Method3():نعم

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

توفر صفائف الهياكل دلالات رائعة.بالنظر إلى RectArray[500] من النوع Rectangle، فمن الواضح والجلي كيفية القيام بذلك على سبيل المثال.انسخ العنصر 123 إلى العنصر 456 ثم قم بعد ذلك بضبط عرض العنصر 123 على 555، دون إزعاج العنصر 456."RectArray[432] = RectArray[321];...;RectArray[123].Width = 555;".إن معرفة أن المستطيل عبارة عن بنية تحتوي على حقل عدد صحيح يسمى Width سيخبرك بكل ما يحتاج المرء إلى معرفته حول العبارات المذكورة أعلاه.

لنفترض الآن أن RectClass كانت فئة تحتوي على نفس الحقول مثل Rectangle وأراد أحدهم إجراء نفس العمليات على RectClassArray[500] من النوع RectClass.ربما من المفترض أن يحتوي المصفوفة على 500 مرجع غير قابل للتغيير تم تهيئته مسبقًا لكائنات RectClass القابلة للتغيير.في هذه الحالة، سيكون الكود المناسب مثل "RectClassArray[321].SetBounds(RectClassArray[456]);...;RectClassArray[321].X = 555;".ربما يُفترض أن المصفوفة تحتوي على مثيلات لن تتغير، لذا فإن الكود المناسب سيكون أشبه بـ "RectClassArray[321] = RectClassArray[456];...;RectClassArray[321] = New RectClass(RectClassArray[321]);RectClassArray[321].X = 555;" لمعرفة ما يفترض أن يفعله المرء، يجب على المرء أن يعرف الكثير عن RectClass (على سبيل المثال.هل يدعم مُنشئ النسخ، أو طريقة النسخ من، وما إلى ذلك) والاستخدام المقصود للمصفوفة.لا يوجد مكان قريب من النظافة مثل استخدام البنية.

من المؤكد أنه لا توجد للأسف طريقة لطيفة لأي فئة حاوية بخلاف المصفوفة لتقديم الدلالات النظيفة لمصفوفة البنية.أفضل ما يمكن أن يفعله المرء، إذا أراد أن تتم فهرسة المجموعة على سبيل المثال.سلسلة، من المحتمل أن تقدم طريقة "ActOnItem" عامة والتي تقبل سلسلة للفهرس، ومعلمة عامة، ومفوض يتم تمريره حسب المرجع لكل من المعلمة العامة وعنصر المجموعة.من شأن ذلك أن يسمح بنفس الدلالات تقريبًا مثل صفائف البنية، ولكن ما لم يتم إقناع موظفي vb.net وC# بتقديم بناء جملة لطيف، فإن الكود سيكون ذو مظهر عتيق حتى لو كان أداءه معقولًا (تمرير معلمة عامة من شأنه أن يسمح بذلك) السماح باستخدام مفوض ثابت وسيتجنب أي حاجة لإنشاء أي مثيلات فئة مؤقتة).

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

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

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

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

هناك حالات ركنية أخرى يمكن أن تؤدي إلى سلوك غير متوقع من وجهة نظر المبرمجين.هنا زوجين منهم.

  1. أنواع القيم غير القابلة للتغيير والحقول للقراءة فقط

// Simple mutable structure. 
// Method IncrementI mutates current state.
struct Mutable
{
    public Mutable(int i) : this() 
    {
        I = i;
    }

    public void IncrementI() { I++; }

    public int I {get; private set;}
}

// Simple class that contains Mutable structure
// as readonly field
class SomeClass 
{
    public readonly Mutable mutable = new Mutable(5);
}

// Simple class that contains Mutable structure
// as ordinary (non-readonly) field
class AnotherClass 
{
    public Mutable mutable = new Mutable(5);
}

class Program
{
    void Main()
    {
        // Case 1. Mutable readonly field
        var someClass = new SomeClass();
        someClass.mutable.IncrementI();
        // still 5, not 6, because SomeClass.mutable field is readonly
        // and compiler creates temporary copy every time when you trying to
        // access this field
        Console.WriteLine(someClass.mutable.I);

        // Case 2. Mutable ordinary field
        var anotherClass = new AnotherClass();
        anotherClass.mutable.IncrementI();

        //Prints 6, because AnotherClass.mutable field is not readonly
        Console.WriteLine(anotherClass.mutable.I);
    }
}

  1. أنواع ومصفوفات القيمة القابلة للتغيير

لنفترض أن لدينا مصفوفة من بنيتنا القابلة للتغيير ونقوم باستدعاء طريقة IncrementI للعنصر الأول من تلك المصفوفة.ما هو التصرف الذي تتوقعه من هذه المكالمة؟هل يجب تغيير قيمة المصفوفة أم نسخة فقط؟

Mutable[] arrayOfMutables = new Mutable[1];
arrayOfMutables[0] = new Mutable(5);

// Now we actually accessing reference to the first element
// without making any additional copy
arrayOfMutables[0].IncrementI();

//Prints 6!!
Console.WriteLine(arrayOfMutables[0].I);

// Every array implements IList<T> interface
IList<Mutable> listOfMutables = arrayOfMutables;

// But accessing values through this interface lead
// to different behavior: IList indexer returns a copy
// instead of an managed reference
listOfMutables[0].IncrementI(); // Should change I to 7

// Nope! we still have 6, because previous line of code
// mutate a copy instead of a list value
Console.WriteLine(listOfMutables[0].I);

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

إذا كنت قد برمجت من أي وقت مضى في لغة مثل C / C ++، البنيات على ما يرام لاستخدام قابلة للتغيير كما. مجرد تمريرها مع المرجع، حول وليس هناك ما يمكن ان تتعرض له. المشكلة الوحيدة أجد هي القيود المفروضة على C # مترجم وأنه في بعض الحالات، أنا غير قادر على إجبار شيء غبي لاستخدام إشارة إلى البنية، بدلا من نسخة (مثل عندما البنية هي جزء من C # الطبقة ).

وهكذا، البنيات قابلة للتغيير ليست شريرة، C # لديها <م> قدم لهم الشر. يمكنني استخدام البنيات قابلة للتغيير في C ++ في كل وقت وأنها مريحة جدا وبديهية. في المقابل، فقد جعلني C # لتتخلى تماما البنيات كأعضاء في الطبقات بسبب الطريقة التي تتعامل بها الكائنات. يناسبهم لقد دفعنا غاليا ثمن لنا.

وتخيل لديك مجموعة من 1،000،000 البنيات. كل البنية يمثل المساواة مع الاشياء مثل bid_price، OFFER_PRICE (ربما عشرية) وهلم جرا، يتم إنشاء هذا عن طريق C # / VB.

وتخيل أن يتم إنشاء مجموعة في كتلة من الذاكرة المخصصة في كومة غير المدارة حتى أن بعض موضوع التعليمات البرمجية الأصلية الآخر هو قادرا على الوصول في وقت واحد لمجموعة (ربما بعض الرياضيات كود عالية الأداء الإقتصادي الأداء بالممارسة).

وتخيل C # رمز / VB يستمع إلى تغذية السوق من تغيرات الأسعار، قد يكون هذا الرمز للوصول إلى بعض العناصر من مجموعة (لأيهما الأمن) ثم قم بتعديل بعض الحقول السعر (ق).

وتخيل يجري هذا عشرات أو حتى مئات الآلاف من المرات في الثانية الواحدة.

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

وهوغو

إذا كنت عصا لما المقصود البنيات ل (في C # و Visual Basic 6، باسكال / دلفي، C ++ نوع البنية (أو الطبقات) عندما لا يتم استخدامها كمؤشرات)، وسوف تجد أن هيكل ليس أكثر من و<م> متغير مركب . وهذا يعني: سوف يعاملهم مجموعة معبأة من المتغيرات، تحت الاسم الشائع (متغير قياسي لك مرجع أعضاء من)

وأنا أعلم أن من شأنه أن يربك الكثير من الناس تستخدم بشدة OOP، ولكن هذا لا يكفي سببا ليقول مثل هذه الأمور الشريرة بطبيعتها، إذا ما استخدمت بشكل صحيح. بعض الهياكل هي inmutable أنها تنوي (وهذا هو الحال بالنسبة للnamedtuple بايثون)، وإنما هو نموذج آخر للنظر فيها.

ونعم: البنيات تنطوي على الكثير من الذاكرة، لكنها لن تكون أكثر تحديدا الذاكرة عن طريق القيام:

point.x = point.x + 1

ومقارنة:

point = Point(point.x + 1, point.y)

واستهلاك الذاكرة سوف تكون على الأقل واحدة، أو أكثر في حالة inmutable (على الرغم من أن هذه الحالة سيكون مؤقتا، لكومة الحالي، اعتمادا على اللغة).

ولكن، في النهاية، الهياكل <م> الهياكل ، وليس الكائنات. في POO، الخاصية الرئيسية للكائن هي من هوية ، والذي أكثر من مرة ليست أكثر من عنوان الذاكرة. البنية تقف على بنية بيانات (وليس الهدف الصحيح، وذلك ليس لديهم هوية على أية حال)، ويمكن تعديل البيانات. في لغات أخرى، <م> سجل (بدلا من البنية ، كما هو الحال بالنسبة لباسكال) هي الكلمة ويحمل نفس الغرض: مجرد متغير سجل البيانات، وتهدف إلى أن تقرأ من الملفات، تعديل، والتي تصب في ملفات (وهذا هو الاستخدام الرئيسي و، في العديد من اللغات، حتى يمكنك تحديد محاذاة البيانات في السجل، في حين أن هذا ليس بالضرورة هو الحال بالنسبة دعا صحيح الأجسام).

هل تريد مثالا جيدا؟ وتستخدم البنيات لقراءة الملفات بسهولة. بيثون ديه هذه المكتبة لأنه، نظرا لأنه من وجوه المنحى وليس لديها دعم البنيات، وكان ل تنفيذ ذلك بطريقة أخرى، وهي قبيحة إلى حد ما. اللغات تنفيذ البنيات لها هذه الميزة ... مدمجة. محاولة قراءة رأس نقطية مع البنية المناسبة في لغات مثل باسكال أو C. وسيكون من السهل (إذا تم بناء البنية بشكل صحيح والانحياز، في باسكال كنت لا تستخدم الوصول القائم على سجل ولكن وظائف لقراءة البيانات الثنائية التعسفي). لذلك، للملفات و(المحلي) الوصول المباشر للذاكرة، البنيات هي أفضل من الكائنات. أما بالنسبة إلى اليوم، ونحن معتادون على JSON وXML، ولذا فإننا ننسى استخدام الملفات الثنائية (وكأثر جانبي، واستخدام البنيات). ولكن نعم: وجدت، ويكون لها هدف

.

وانهم ليسوا الشر. مجرد استخدامها لغرض الصحيح.

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

عندما يمكن تحور شيء ما، فإنه يكتسب إحساسًا بالهوية.

struct Person {
    public string name; // mutable
    public Point position = new Point(0, 0); // mutable

    public Person(string name, Point position) { ... }
}

Person eric = new Person("Eric Lippert", new Point(4, 2));

لأن Person قابل للتغيير، ومن الطبيعي التفكير فيه تغيير موقف إريك من استنساخ إريك وتحريك النسخة المستنسخة وتدمير الأصل.ستنجح كلتا العمليتين في تغيير محتويات eric.position, ، ولكن أحدهما أكثر بديهية من الآخر.وبالمثل، من السهل جدًا تمرير إريك (كمرجع) للتعرف على طرق تعديله.إن إعطاء طريقة لاستنساخ إريك سيكون دائمًا أمرًا مفاجئًا.أي شخص يريد أن يتحول Person يجب أن نتذكر أن نسأل عن إشارة إلى Person أو أنهم سوف يفعلون الشيء الخطأ.

إذا جعلت النوع غير قابل للتغيير، فستختفي المشكلة؛إذا لم أتمكن من تعديل eric, ، لا فرق بالنسبة لي إذا كنت أتلقى eric أو استنساخ eric.بشكل أكثر عمومية، يكون النوع آمنًا لتمريره حسب القيمة إذا كانت حالته القابلة للملاحظة موجودة في أعضاء إما:

  • غير قابل للتغيير
  • أنواع المراجع
  • آمنة لتمرير القيمة

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

بديهة غير قابلة للتغيير Person يعتمد على ما تحاول القيام به بالرغم من ذلك.لو Person يمثل فقط أ مجموعة من البيانات فيما يتعلق بشخص ما، لا يوجد شيء غير بديهي فيه؛ Person المتغيرات تمثل حقا مجردة قيم, وليس الكائنات.(في هذه الحالة، قد يكون من الأفضل إعادة تسميته إلى PersonData.) لو Person هو في الواقع نمذجة الشخص نفسه، فإن فكرة إنشاء النسخ المستنسخة ونقلها باستمرار هي فكرة سخيفة حتى لو تجنبت الوقوع في مأزق التفكير في أنك تقوم بتعديل النسخة الأصلية.في هذه الحالة ربما يكون من الطبيعي أن يتم ذلك ببساطة Person نوع مرجعي (أي فئة.)

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

وأنه ليس لديه أي علاقة مع البنيات (وليس مع C #، إما) ولكن في جاوة قد تحصل مشاكل مع كائنات قابلة للتغيير عندما تكون على سبيل المثال مفاتيح في خريطة التجزئة. إذا قمت بتغيير لهم بعد إضافتها إلى خريطة ويتغير لها <لأ href = "http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Object.html#hashCode () "يختلط =" noreferrer "> رمز التجزئة و المنكرات قد يحدث.

وشخصيا عندما ننظر إلى رمز التالية يبدو عالي الكعب جدا بالنسبة لي:

وdata.value.set (data.value.get () + 1)؛

وبدلا من مجرد

وdata.value ++؛ أو data.value = data.value + 1؛

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

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

للي هذا يمنع استخدام صالحة من الهياكل التي تستخدم لتنظيم المتغيرات العامة، إذا أردت التحكم في الوصول فما استقاموا لكم فاستقيموا استخدام فئة.

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

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

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

التنفيذ السليم سيكون على النحو التالي.

struct Mutable {
public int x;
}

class Test {
    private Mutable m = new Mutable();
    public int mutate()
    { 
        m.x = m.x + 1;
        return m.x;
    }
  }
  static void Main(string[] args) {
        Test t = new Test();
        System.Console.WriteLine(t.mutate());
        System.Console.WriteLine(t.mutate());
        System.Console.WriteLine(t.mutate());
    }

يبدو أنها مشكلة في عادة البرمجة وليس مشكلة في البنية نفسها.من المفترض أن تكون الهياكل قابلة للتغيير، هذه هي الفكرة والقصد.

نتيجة التغييرات يتصرف فويلا كما هو متوقع:

1 2 3 اضغط على أي مفتاح للمتابعة...

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

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

فن الترجمة في يذهب الى هذه المفاضلة في بعض التفاصيل، ويعطي بعض الأمثلة على ذلك.

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

وباستخدام المثال اريك، ربما كنت ترغب في إنشاء مثيل من أن اريك، ولكن إجراء تعديلات، وهذا هو طبيعة الاختبار (أي ازدواجية، ثم تعديل). لا يهم ما يحدث مع المثيل الأول من اريك إذا نحن فقط باستخدام Eric2 للفترة المتبقية من البرنامج النصي اختبار، إلا إذا كنت تخطط لاستخدام له وعلى سبيل المقارنة الاختبار.

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

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