في C#، أين يمكنك استخدام "ref" أمام المعلمة؟

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

  •  03-07-2019
  •  | 
  •  

سؤال

هناك عدد من الأسئلة بالفعل حول تعريف المعلمة "ref" و"out" ولكنها تبدو وكأنها تصميم سيء.هل هناك أي حالات تعتقد أن المرجع هو الحل الصحيح؟

يبدو أنه يمكنك دائمًا القيام بشيء آخر أكثر نظافة.هل يمكن لأحد أن يعطيني مثالاً على المكان الذي سيكون فيه هذا هو الحل "الأفضل" لمشكلة ما؟

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

المحلول

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

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

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

نصائح أخرى

حسنا، يتم استخدام ref عموما لحالات المتخصصة، لكنني لن نسميها زائدة عن الحاجة أو ميزة إرث C #. سترى ذلك (وout) تستخدم كثيرا في XNA على سبيل المثال. في XNA، وMatrix هو struct واحد ضخم وليس في ذلك (وأعتقد 64 بايت) وانها عموما الأفضل إذا كنت تمريرها إلى وظائف باستخدام ref لتجنب نسخ 64 بايت، ولكن فقط 4 أو 8. اختصاصي C # الميزة؟ من المؤكد. لا تستخدم أي أكثر بكثير أو مؤشرا على تصميم سيئة؟ أنا لا أوافق.

ومنطقة واحدة هي في استخدام وظائف الأداة الصغيرة، مثل:

void Swap<T>(ref T a, ref T b) { T tmp = a; a = b; b = tmp; }  

وأنا لا أرى أي بدائل "أنظف" هنا. منح، وهذا ليس بالضبط مستوى العمارة.

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

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

void GetXYZ( ref object x, ref object y, ref object z);

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

وأعتقد أن أفضل الاستخدامات هي تلك التي ترى عادة. تحتاج إلى أن يكون كل من قيمة و"مؤشر نجاح" ليست استثناء من دالة.

نمط تصميم واحد حيث ref مفيد هو زائر ثنائي الاتجاه.

لنفترض أن لديك Storage فئة يمكن استخدامها لتحميل أو حفظ قيم الأنواع البدائية المختلفة.فهو إما في Load الوضع أو Save وضع.لديها مجموعة من الأساليب المحملة بشكل زائد تسمى Transfer, ، وإليك مثال للتعامل معها int قيم.

public void Transfer(ref int value)
{
    if (Loading)
        value = ReadInt();
    else
        WriteInt(value);
}

ستكون هناك طرق مماثلة للأنواع البدائية الأخرى - bool, string, ، إلخ.

ثم في الفصل الذي يجب أن يكون "قابلاً للتحويل"، ستكتب طريقة مثل هذا:

public void TransferViaStorage(Storage s)
{
    s.Transfer(ref _firstName);
    s.Transfer(ref _lastName);
    s.Transfer(ref _salary);
}

يمكن لهذه الطريقة الفردية إما تحميل الحقول من ملف Storage, أو احفظ الحقول في Storage, ، اعتمادا على الوضع Storage الكائن في.

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

النقطة العامة هي أنه عندما يتم وضع علامة على المعلمة ref, ، فأنت لا تعرف ما إذا كانت الطريقة ستقرأها أم تكتب إليها، وهذا يسمح لك بتصميم فئات زائر تعمل في أحد الاتجاهين، والمقصود أن يتم استدعاؤها بطريقة متماثلة (أي:مع عدم حاجة الطريقة التي تمت زيارتها إلى معرفة وضع الاتجاه الذي تعمل فيه فئة الزائر).

مقارنة:السمات + الانعكاس

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

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

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

وهذا سبب واضح لاستخدام "المرجع" الكلمة هو عندما تريد تمرير متغير حسب المرجع. على سبيل المثال تمرير نوع قيمة مثل System.Int32 إلى طريقة وتغيير فإنه من القيمة الفعلية. واستخدام أكثر تحديدا قد يكون عندما تريد مبادلة اثنين من المتغيرات.

public void Swap(ref int a, ref int b)
{
   ...
}

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

public void DoMagic(out int a, out int b, out int c, out int d)
{
   ...
}

وهناك حالة واحدة واضحة عندما يجب أن تستخدم لكم الكلمة "المرجع". إذا تم تعريف الكائن ولكن لم يتم إنشاؤها خارج نطاق الأسلوب الذي كنت تنوي استدعاء والطريقة التي تريد الاتصال من المفترض أن تفعل 'new' لإنشائه، يجب عليك استخدام 'ref'. سوف e.g.{object a; Funct(a);} {Funct(object o) {o = new object; o.name = "dummy";} لا تفعل شيئا مع object 'a' ولن نشكو منها في أي تجمع أو وقت التشغيل. انها فقط لن تفعل أي شيء. سوف {object a; Funct(ref a);} {Funct(object ref o) {o = new object(); o.name = "dummy";} يؤدي إلى 'a' كونه كائن جديد مع اسم "dummy ". ولكن إذا تم إجراء 'new' بالفعل، ثم المرجع لا حاجة (ولكن يعمل إذا مرفق). {object a = new object(); Funct(a);} {Funct(object o) {o.name = "dummy";}

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