تخفي طريقة VS VS [مكررة
-
27-09-2019 - |
سؤال
هذا السؤال لديه بالفعل إجابة هنا:
- الفرق بين التظليل والتجاوز في C#؟ 5 إجابات
أنا مرتبك بعض الشيء بشأن التجاوز مقابل إخفاء طريقة في C#. كما سيكون موضع تقدير الاستخدامات العملية لكل منها ، بالإضافة إلى تفسير ل متى يمكن للمرء استخدام كل.
أنا في حيرة من أمري بشأن التجاوز - لماذا نتغلب؟ ما تعلمته حتى الآن هو أنه من خلال الإفراط في الإفراط في تقديم التنفيذ المرغوب فيه بطريقة فئة مشتقة ، دون تغيير التوقيع.
إذا لم أتجاوز طريقة الطبقة الفائقة وأجري تغييرات على الطريقة في الفصل الفرعي ، فهل سيجري ذلك تغييرات على طريقة الفئة الفائقة؟
أنا أيضًا مرتبك بشأن ما يلي - ماذا يظهر هذا؟
class A
{
virtual m1()
{
console.writeline("Bye to all");
}
}
class B : A
{
override m1()
{
console.writeLine("Hi to all");
}
}
class C
{
A a = new A();
B b = new B();
a = b; (what is this)
a.m1(); // what this will print and why?
b = a; // what happens here?
}
المحلول
انصح:
public class BaseClass
{
public void WriteNum()
{
Console.WriteLine(12);
}
public virtual void WriteStr()
{
Console.WriteLine("abc");
}
}
public class DerivedClass : BaseClass
{
public new void WriteNum()
{
Console.WriteLine(42);
}
public override void WriteStr()
{
Console.WriteLine("xyz");
}
}
/* ... */
BaseClass isReallyBase = new BaseClass();
BaseClass isReallyDerived = new DerivedClass();
DerivedClass isClearlyDerived = new DerivedClass();
isReallyBase.WriteNum(); // writes 12
isReallyBase.WriteStr(); // writes abc
isReallyDerived.WriteNum(); // writes 12
isReallyDerived.WriteStr(); // writes xyz
isClearlyDerived.WriteNum(); // writes 42
isClearlyDerived.writeStr(); // writes xyz
الإفراط هو طريقة OO الكلاسيكية التي يمكن أن يكون لها فئة مشتقة سلوكًا أكثر تحديدًا من فئة أساسية (في بعض اللغات التي لا تختارها سوى القيام بذلك). عندما يتم استدعاء طريقة افتراضية على كائن ، يتم استدعاء الإصدار الأكثر اشتقاقًا من الطريقة. وبالتالي على الرغم من أننا نتعامل مع isReallyDerived
ك BaseClass
ثم يتم تعريف الوظيفة في DerivedClass
يستخدم.
الاختباء يعني أن لدينا طريقة مختلفة تماما. عندما ندعو WriteNum()
على isReallyDerived
ثم لا توجد طريقة لمعرفة أن هناك مجموعة مختلفة WriteNum()
على DerivedClass
لذلك لا يسمى. لا يمكن استدعاؤه إلا عندما نتعامل مع الكائن كما أ DerivedClass
.
معظم الوقت الاختباء أمر سيء. بشكل عام ، إما أن يكون لديك طريقة افتراضية إذا كان من المحتمل أن يتم تغييرها في فئة مشتقة ، وتجاوزها في الفئة المشتقة. ومع ذلك ، هناك شيئان مفيدان لـ:
توافق إلى الأمام. إذا
DerivedClass
كانDoStuff()
الطريقة ، ثم في وقت لاحقBaseClass
تم تغييره لإضافة أDoStuff()
الطريقة ، (تذكر أنه قد يكتبهم أشخاص مختلفين ويوجون في مجموعات مختلفة) ثم كان من الممكن أن يكون هناك حظر على إخفاء الأعضاء فجأةDerivedClass
عربات التي تجرها الدواب دون تغيير. أيضا ، إذا كان الجديدDoStuff()
علىBaseClass
كان افتراضيًا ، ثم صنع ذلك تلقائيًاDerivedClass
قد يؤدي تجاوزها إلى الطريقة الموجودة مسبقًا عندما لا ينبغي ذلك. وبالتالي ، من الجيد أن يكون الاختباء هو الافتراضي (نستخدمهnew
لتوضيح أننا نريد بالتأكيد أن نختبئ ، ولكن تركها تختبئ وينبعث تحذيرًا على التجميع).التباين الفقير. النظر في
Clone()
طريقة علىBaseClass
هذا يعيد جديدBaseClass
هذه نسخة من تلك التي تم إنشاؤها. في التجاوزDerivedClass
هذا سيخلق ملفDerivedClass
لكن إعادته كـBaseClass
, ، وهو ليس مفيدًا. ما يمكن أن نفعله هو أن يكون لديك حماية افتراضيةCreateClone()
تم تجاوز هذا. فيBaseClass
لديناClone()
هذا يعيد نتيجة هذا - وكل شيء على ما يرام - فيDerivedClass
نخفي هذا مع جديدClone()
هذا يعود أDerivedClass
. الدعوةClone()
علىBaseClass
سيعود دائمًاBaseClass
المرجع ، الذي سيكونBaseClass
القيمة أوDerivedClass
القيمة حسب الاقتضاء. الدعوةClone()
علىDerivedClass
سيعود أDerivedClass
القيمة ، وهو ما نريده في هذا السياق. هناك متغيرات أخرى لهذا المبدأ ، ومع ذلك تجدر الإشارة إلى أنها جميعها نادرة جدًا.
شيء مهم يجب ملاحظته في الحالة الثانية ، هو أننا استخدمنا الاختباء على وجه التحديد إزالة يفاجئ رمز الاتصال ، كشخص يستخدم DerivedClass
قد تتوقع بشكل معقول Clone()
لإرجاع أ DerivedClass
. يتم الحفاظ على نتائج أي من الطرق التي يمكن استدعاؤها بما يتوافق مع بعضها البعض. معظم حالات إخفاء مخاطر إدخال المفاجآت ، وهذا هو السبب في أنها متعبئة بشكل عام. هذا ما يبرره بالضبط لأنه يحل المشكلة ذاتها التي يختبئها في كثير من الأحيان.
إجمالاً ، يكون الاختباء ضروريًا في بعض الأحيان ، مفيد بشكل غير متكرر ، ولكن سيئًا بشكل عام ، لذا كن حذرًا جدًا منه.
نصائح أخرى
الإفراط هو عندما تقدم جديدًا override
تنفيذ طريقة في فئة سلي virtual
.
الاختباء هو عندما تقدم تنفيذًا جديدًا لأسلوب ما في فئة سليل عندما تكون هذه الطريقة ليس محددة في الفئة الأساسية virtual
, ، أو عندما لا يحدد تطبيقك الجديد override
.
الاختباء في كثير من الأحيان سيء. يجب أن تحاول عمومًا ألا تفعل ذلك إذا تمكنت من تجنب ذلك على الإطلاق. يمكن أن يتسبب الاختباء في حدوث أشياء غير متوقعة ، لأن الأساليب المخفية تستخدم فقط عند استدعاء متغير من النوع الفعلي الذي حددته ، وليس إذا كان استخدام مرجع فئة أساسية ... من ناحية أخرى ، ستنتهي الطرق الافتراضية التي يتم تجاوزها يتم استدعاء إصدار الطريقة المناسبة ، حتى عند استدعاء مرجع الفئة الأساسية على فئة الطفل.
على سبيل المثال ، فكر في هذه الفئات:
public class BaseClass
{
public virtual void Method1() //Virtual method
{
Console.WriteLine("Running BaseClass Method1");
}
public void Method2() //Not a virtual method
{
Console.WriteLine("Running BaseClass Method2");
}
}
public class InheritedClass : BaseClass
{
public override void Method1() //Overriding the base virtual method.
{
Console.WriteLine("Running InheritedClass Method1");
}
public new void Method2() //Can't override the base method; must 'new' it.
{
Console.WriteLine("Running InheritedClass Method2");
}
}
دعنا نسميها هكذا ، مع مثيل للورث ، في إشارة مطابقة:
InheritedClass inherited = new InheritedClass();
inherited.Method1();
inherited.Method2();
هذا يعيد ما يجب أن تتوقعه ؛ تقول كلتا الطريقتين إنهما يديران إصدارات الموروثة.
تشغيل methodclass method1
تشغيل methodclass method2
ينشئ هذا الرمز مثيلًا لنفسه ، الموروثة ، ولكنه يخزنه في مرجع أساسي:
BaseClass baseRef = new InheritedClass();
baseRef.Method1();
baseRef.Method2();
عادة ، بموجب مبادئ OOP ، يجب أن تتوقع نفس الإخراج مثل المثال أعلاه. لكنك لا تحصل على نفس الإخراج:
تشغيل methodclass method1
تشغيل baseclass method2
عندما كتبت رمز الموروثة ، ربما كنت ترغب في جميع المكالمات Method2()
لتشغيل الرمز الذي كتبته فيه. عادة ، سيكون هذا هو ما يعمل - على افتراض أنك تعمل مع أ virtual
الطريقة التي تجاوزتها. ولكن لأنك تستخدم new
/الطريقة المخفية ، يقوم باستدعاء الإصدار على المرجع الذي تستخدمه ، بدلاً من ذلك.
إذا كان هذا هو السلوك الذي حقا تريد, ، ومن بعد؛ ها أنت ذا. لكنني أقترح بشدة أنه إذا كان هذا هو ما تريده ، فقد تكون هناك مشكلة معمارية أكبر مع الكود.
الطريقة الغالبة هي تجاوز التنفيذ الافتراضي لطريقة فئة قاعدة في الفئة المشتقة.
طريقة إخفاء: يمكنك استخدام الكلمة الرئيسية "الجديدة" قبل طريقة افتراضية في فئة مشتقة
كما
class Foo
{
public virtual void foo1()
{
}
}
class Bar:Foo
{
public new virtual void foo1()
{
}
}
الآن إذا قمت بإنشاء BAR1 فئة أخرى مشتقة من BAR ، فيمكنك تجاوز FOO1 الذي يتم defind in bar.