غلاف قابل للتغيير لأنواع القيمة لتمريرها إلى التكرارات

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

سؤال

أنا أكتب مكررًا يحتاج إلى تمرير عدد صحيح قابل للتغيير.

public IEnumerable<T> Foo(ref int valueThatMeansSomething)
{
    // Stuff

    yield return ...;
}

يؤدي هذا إلى ظهور رسالة "خطأ 476، لا يمكن أن تحتوي التكرارات على معلمات مرجعية أو خارجية".

ما أحتاجه هو أن يتم تعديل قيمة العدد الصحيح هذه في المُكرِّر وتكون قابلة للاستخدام من قبل المتصل بالمكرِّر.وبعبارة أخرى، أيا كان ما يدعو Foo() أعلاه يريد أن يعرف القيمة النهائية ل valueThatMeansSomething و Foo() يجوز استخدامه في حد ذاته.حقًا، أريد عددًا صحيحًا يمثل نوعًا مرجعيًا وليس نوع قيمة.

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

public class ValueWrapper<T>
    where T : struct
{
    public ValueWrapper(T item)
    {
        this.Item = item;
    }

    public T Item { get; set; }
}

لذا:

ValueWrapper<int> w = new ValueWrapper<int>(0);
foreach(T item in Foo(w))
{
    // Do stuff
}

if (w.Item < 0) { /* Do stuff */ }

هل هناك أي فئة أو آلية للتعامل مع هذا بالفعل في BCL؟ أي عيوب بها ValueWrapper<T> المقترحة أعلاه؟

(استخدامي الفعلي أكثر تعقيدًا من المثال أعلاه، لذا فإن التعامل مع المتغير الموجود داخل ملفي foreach الحلقة التي تدعو Foo() ليس خيارا.فترة.)

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

المحلول

لا، أنا واثق تمامًا أنه لا يوجد شيء موجود في BCL يمكنه القيام بذلك.أفضل خيار لك هو بالضبط ما اقترحته على ما أعتقد.تنفيذ ValueWrapper لا يلزم حقًا أن يكون الأمر أكثر تعقيدًا مما اقترحته.

بالطبع، ليس من المضمون أن تكون آمنة لسلسلة الرسائل، ولكن إذا كنت بحاجة إلى ذلك، يمكنك ببساطة تحويل الخاصية التلقائية إلى خاصية قياسية مع متغير دعم ووضع علامة على الحقل كـ volatile (للتأكد من أن القيمة محدثة في جميع الأوقات).

نصائح أخرى

إذا كنت تحتاج فقط إلى كتابة القيمة، فسيكون الأسلوب الآخر هو:

public IEnumerable<whatever> Foo(Action<int> setter) { ... }

int value = 0;
foreach(var x in Foo(x => {value=x;}) { ... }

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

http://blogs.msdn.com/ericlippert/archive/tags/Iterators/default.aspx

لقد اعتقدت منذ فترة طويلة أن BCL يجب أن يكون لها فئة وواجهة مثل ما يلي:

public delegate void ActByRef<T1,T2>(ref T1 p1);
public delegate void ActByRefRef<T1,T2>(ref T1 p1, ref T2 p2);
public interface IReadWriteActUpon<T>
{
  T Value {get; set;}
  void ActUpon(ActByRef<T> proc);
  void ActUpon<TExtraParam>(ActByRefRef<T, TExtraParam> proc, 
                           ref TExtraparam ExtraParam);
}

public sealed class MutableWrapper<T> : IReadWrite<T>
{
  public T Value;
  public MutableWrapper(T value) { this.Value = value; }
  T IReadWriteActUpon<T>.Value {get {return this.Value;} set {this.Value = value;} }
  public void ActUpon(ActByRef<T> proc)
  {
    proc(ref Value);
  }
  public void ActUpon<TExtraParam>(ActByRefRef<T, TExtraParam> proc, 
                           ref TExtraparam ExtraParam)
  {
    proc(ref Value, ref ExtraParam);
  }
}

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

يتم تضمين الواجهة ليس لأن العديد من مستخدمي MutableWrapper<T> قد ترغب في استخدام الواجهة بدلاً من ذلك، بل لأن IReadWriteActUpon<T> يمكن أن يكون مفيدًا في مجموعة متنوعة من المواقف، والتي قد يستلزم بعضها التغليف، وشخص لديه مثال على ذلك MutableWrapper<T> قد ترغب في تمريرها إلى التعليمات البرمجية المصممة للعمل مع البيانات المغلفة في ملف IReadWriteActUpon<T> واجهه المستخدم.

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