سؤال

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

إذن ما أريده هو:

      private _Foo;

      public Foo 
      { 
         get { return readonly _Foo; } 
      } 

.. وهو بالطبع غير صالح.يمكنني فقط إرجاع نسخة من Foo (على افتراض أنه كذلك IClonable)، ولكن هذا ليس واضحا للمستهلك.هل يجب أن أغير اسم العقار إلى FooCopy؟؟هل يجب أن يكون أ GetCopyOfFoo طريقة بدلا من ذلك؟ما الذي تعتبره أفضل الممارسات؟شكرًا!

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

المحلول

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

هل يمكن إرجاع استنساخ من فو كما اقترح، أو ربما وجهة نظر <م> على فو، و<لأ href = "http://msdn.microsoft.com/en-us/library /ms132474.aspx "يختلط =" noreferrer "> ReadOnlyCollection لا للمجموعات. بالطبع إذا كنت يمكن أن تجعل Foo نوع غير قابل للتغيير، من شأنه أن يجعل الحياة أكثر بساطة ...

لاحظ أن هناك فرقا كبيرا بين جعل <م> الحقل للقراءة فقط وجعل الكائن نفسه غير قابل للتغيير.

وحاليا، فإن نوع نفسها يمكن أن تتغير الأمور في كلا الاتجاهين. يمكن أن تفعل:

_Foo = new Foo(...);

أو

_Foo.SomeProperty = newValue;

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

وانها <م> مهم جدا أن تفهم الفرق بين تغيير قيمة حقل (لجعله الرجوع إلى مثيل مختلفة) وتغيير محتويات الكائن أن حقل يشير إلى.

نصائح أخرى

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

وجعل على نسخة منه، على ReadOnlyCollection، أو وجود Foo يكون غير قابل للتغيير وعادة ما تكون أفضل ثلاث طرق، وكنت قد تكهن بالفعل.

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

"استنساخ" كائنات Foo التي تتلقاها وتعيدها هي ممارسة عادية تسمى النسخ الدفاعي.ما لم يكن هناك بعض الآثار الجانبية غير المرئية للاستنساخ والتي ستكون مرئية للمستخدم، فلا يوجد أي سبب على الإطلاق لعدم القيام بذلك.غالبًا ما تكون هذه هي الطريقة الوحيدة لحماية البيانات الخاصة الداخلية لفصولك، خاصة في لغة C# أو Java، حيث فكرة C++ هي const غير متاح.(أي، يجب أن يتم ذلك من أجل إنشاء كائنات غير قابلة للتغيير بشكل صحيح في هاتين اللغتين.)

فقط للتوضيح، قد تكون الآثار الجانبية المحتملة أشياء مثل توقع المستخدم (بشكل معقول) إرجاع الكائن الأصلي، أو بعض الموارد التي يحتفظ بها Foo والتي لن يتم استنساخها بشكل صحيح.(في هذه الحالة، ما الذي تفعله بتنفيذ IClonable؟!)

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

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

class Foo{
 public string A{get; set;}
 public string B{get; set;}
 //...
}

class ReadOnlyFoo{
  Foo foo; 
  public string A { get { return foo.A; }}
  public string B { get { return foo.B; }}
}

ويمكنك إعادة فعلا سلوك C ++ CONST في C # - عليك أن تفعل ذلك يدويا

.

ومهما Foo هو، والطريقة الوحيدة التي يمكن للطالب تعديل حالته هي عن طريق استدعاء أساليب عليه أو تحديد خصائص.

وعلى سبيل المثال، Foo هو من نوع FooClass:

class FooClass
{
    public void MutateMyStateYouBadBoy() { ... }

    public string Message
    {
        get { ... }
        set { ... }
    }
}

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

وحتى تحديد واجهة تصف ما كنت يسمح القيام به:

interface IFooConst
{
    public string Message
    {
        get { ... }
    }
}

لقد استبعد طريقة تحور وإلا تركت في جالبة على الممتلكات.

وبعد ذلك يمكنك إضافة واجهة إلى قائمة قاعدة FooClass.

والآن في صفك مع الخاصية Foo، لديك حقل:

private FooClass _foo;

وجالبة الملكية:

public IFooConst Foo
{
    get { return _foo; }
}

وهذا يستنسخ أساسا باليد بالضبط ما C ++ const الكلمة ستفعل تلقائيا. من حيث الزائف-C ++، في اشارة من نوع const Foo & مثل نوع إنشاؤه تلقائيا يتضمن فقط أولئك الأعضاء من Foo التي اتسمت كأعضاء const. ترجمة هذا إلى نسخة النظري للC # المستقبل، وكنت أعلن FooClass مثل هذا:

class FooClass
{
    public void MutateMyStateYouBadBoy() { ... }

    public string Message
    {
        get const { ... }
        set { ... }
    }
}

وحقا كل ما قمت به هو دمج المعلومات في IFooConst العودة الى FooClass، عن طريق وضع علامات عضو واحد آمن مع الكلمة const جديدة. حتى في الطريق، إضافة الكلمة CONST لن تضيف الكثير إلى اللغة إلى جانب النهج الرسمي لهذا النمط.

وبعد ذلك إذا كان لديك إشارة const إلى كائن FooClass:

const FooClass f = GetMeAFooClass();

هل سيكون فقط قادرا على استدعاء أعضاء CONST على f.

ملاحظة أنه إذا كان تعريف FooClass هو الجمهور، المتصل يمكن أن يلقي على IFooConst إلى FooClass. ولكنها تستطيع أن تفعل ذلك في C ++ جدا - انه دعا "صب بعيدا const" وينطوي على مشغل خاص يسمى const_cast<T>(const T &)

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

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

وهذا هو رمز الزائفة، ولكن آمل أن الفكرة من وراء ذلك واضحة

public delegate void OnlyRuller(string s1, string s2);
public delegate void RullerCoronation(OnlyRuller d);

class Foo {
  private Foo();
  public Foo(RullerCoronation followMyOrders) {
     followMyOrders(SetMe);
  }

  private SetMe(string whatToSet, string whitWhatValue) {
     //lot of unclear but private code
  }
}

وهكذا في الصف الذي يخلق هذا العقار لديك حق الوصول إلى طريقة SetMe، لكنه ما زال الخاص بذلك باستثناء الخالق يبدو فو unmutable.

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

ولكن، وكما قلت، وهذا هو نظرية فقط.

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