C# - استخدام الكلمات الرئيسية افتراضي + تجاوز مقابل.جديد
-
03-07-2019 - |
سؤال
ما هي الاختلافات بين الإعلان عن طريقة في النوع الأساسي "virtual
"ثم تجاوزه في نوع تابع باستخدام"override
"الكلمة الرئيسية بدلاً من مجرد استخدام"new
"الكلمة الأساسية عند الإعلان عن طريقة المطابقة في النوع الفرعي؟
المحلول
لا يتم تجاوز الكلمة الأساسية "جديدة"، فهي تشير إلى طريقة جديدة لا علاقة لها بطريقة الفئة الأساسية.
public class Foo
{
public bool DoSomething() { return false; }
}
public class Bar : Foo
{
public new bool DoSomething() { return true; }
}
public class Test
{
public static void Main ()
{
Foo test = new Bar ();
Console.WriteLine (test.DoSomething ());
}
}
هذا يطبع خطأ، إذا استخدمت التجاوز لكان قد طبع صحيحًا.
(الرمز الأساسي مأخوذ من جوزيف ديجل)
لذلك، إذا كنت تفعل تعدد الأشكال الحقيقي لك ينبغي دائما تجاوز.المكان الوحيد الذي تحتاج فيه إلى استخدام "جديد" هو عندما لا تكون الطريقة مرتبطة بأي شكل من الأشكال بإصدار الفئة الأساسية.
نصائح أخرى
أجد دائمًا أشياء مثل هذه يسهل فهمها باستخدام الصور:
مرة أخرى، بأخذ كود جوزيف ديجل،
public class Foo
{
public /*virtual*/ bool DoSomething() { return false; }
}
public class Bar : Foo
{
public /*override or new*/ bool DoSomething() { return true; }
}
إذا قمت بعد ذلك باستدعاء الرمز مثل هذا:
Foo a = new Bar();
a.DoSomething();
ملحوظة:الشيء المهم هو أن الجسم الذي لدينا هو في الواقع أ Bar
, ، لكنا كذلك تخزينه في متغير من النوع Foo
(وهذا يشبه صب ذلك)
ثم ستكون النتيجة على النحو التالي، اعتمادا على ما إذا كنت قد استخدمت virtual
/override
أو new
عندما تعلن عن الفصول الدراسية الخاصة بك.
وفيما يلي بعض التعليمات البرمجية لفهم الفرق في سلوك الطرق الافتراضية وغير الافتراضية:
class A
{
public void foo()
{
Console.WriteLine("A::foo()");
}
public virtual void bar()
{
Console.WriteLine("A::bar()");
}
}
class B : A
{
public new void foo()
{
Console.WriteLine("B::foo()");
}
public override void bar()
{
Console.WriteLine("B::bar()");
}
}
class Program
{
static int Main(string[] args)
{
B b = new B();
A a = b;
a.foo(); // Prints A::foo
b.foo(); // Prints B::foo
a.bar(); // Prints B::bar
b.bar(); // Prints B::bar
return 0;
}
}
والكلمة new
يخلق في الواقع عضوا جديدا تماما ما هو موجود فقط في ذلك نوع معين.
وعلى سبيل المثال
public class Foo
{
public bool DoSomething() { return false; }
}
public class Bar : Foo
{
public new bool DoSomething() { return true; }
}
وهذه الطريقة موجودة على كلا النوعين. عند استخدام انعكاس والحصول على أعضاء نوع Bar
، وسوف تجد فعلا 2 أساليب دعا DoSomething()
التي تبدو بالضبط نفس الشيء. باستخدام new
قمت بإخفاء بفعالية تنفيذ في الفئة الأساسية، بحيث عندما تستمد فئات من Bar
(في بلدي على سبيل المثال) استدعاء الأسلوب إلى base.DoSomething()
يذهب إلى Bar
وليس Foo
.
الظاهري / التجاوز يخبر المترجم أن الطريقتين مرتبطتان وأنه في بعض الحالات عندما تعتقد أنك تتصل بالطريقة الأولى (الافتراضية)، فمن الصحيح بالفعل استدعاء الطريقة الثانية (المتجاوزة) بدلاً من ذلك.هذا هو أساس تعدد الأشكال.
(new SubClass() as BaseClass).VirtualFoo()
سيتم استدعاء أسلوب VirtualFoo() الذي تم تجاوزه في الفئة الفرعية.
جديد يخبر المترجم أنك تقوم بإضافة طريقة إلى فئة مشتقة بنفس اسم الطريقة الموجودة في الفئة الأساسية، لكن ليس لديهما علاقة ببعضهما البعض.
(new SubClass() as BaseClass).NewBar()
سيتم استدعاء طريقة NewBar () الخاصة بـ BaseClass، بينما:
(new SubClass()).NewBar()
سيتم استدعاء أسلوب NewBar () الخاص بـ SubClass.
وراء مجرد التفاصيل التقنية، وأعتقد أن استخدام الظاهري / تجاوز يتصل الكثير من المعلومات الدلالي على التصميم. عندما تقوم بتعريف طريقة الظاهري، فإنك تشير إلى تتوقع أن الفصول تنفيذ قد ترغب أن تقدم تطبيقات غير الافتراضية الخاصة بهم. حذف هذا في فئة أساسية، وبالمثل، يعلن توقع أن يجب الأسلوب الافتراضي ليكفي لجميع الطبقات المنفذة. وبالمثل، يمكن للمرء استخدام الإعلانات مجردة لإجبار الطبقات المنفذة أن تقدم التنفيذ الخاصة بها. مرة أخرى، وأعتقد أن هذا يتصل الكثير عن كيف يتوقع مبرمج رمز لاستخدامها. لو كنت أكتب كل من القاعدة وتنفيذ الطبقات وجدت نفسي باستخدام الجديد فما استقاموا لكم فاستقيموا إعادة التفكير بجدية في قرار عدم جعل طريقة الظاهري في الأصل وتعلن هدفي على وجه التحديد.
new
الكلمة الرئيسية للاختباء.- يعني أنك تخفي طريقتك في وقت التشغيل.سوف يعتمد الإخراج على طريقة الفئة الأساسية.override
للتجاوز.- يعني أنك تستدعي طريقة الفئة المشتقة الخاصة بك مع مرجع الفئة الأساسية.سوف يعتمد الإخراج على طريقة الفئة المشتقة.
نسختي من الشرح تأتي من استخدام ملكيات للمساعدة في فهم الاختلافات.
override
بسيطة بما فيه الكفاية، أليس كذلك؟النوع الأساسي يتجاوز الوالدين.
new
ربما يكون مضللاً (بالنسبة لي كان).من الأسهل فهم الخصائص:
public class Foo
{
public bool GetSomething => false;
}
public class Bar : Foo
{
public new bool GetSomething => true;
}
public static void Main(string[] args)
{
Foo foo = new Bar();
Console.WriteLine(foo.GetSomething);
Bar bar = new Bar();
Console.WriteLine(bar.GetSomething);
}
باستخدام مصحح الأخطاء يمكنك ملاحظة ذلك Foo foo
لديه 2 GetSomething
الخصائص، حيث أنها تحتوي في الواقع على نسختين من الخاصية، Foo
'رمل Bar
's، ولمعرفة أي منها يجب استخدامه، يقوم c# "باختيار" الخاصية للنوع الحالي.
إذا كنت ترغب في استخدام إصدار الشريط، كنت قد استخدمت التجاوز أو الاستخدام Foo foo
بدلاً من.
Bar bar
عنده فقط 1, كما يريد تماما جديد السلوك ل GetSomething
.
لا تدل على الأسلوب مع أي شيء يعني: ربط هذه الطريقة باستخدام نوع الترجمة للكائن، وليس وقت التشغيل نوع (ثابت ملزم).
ووسم طريقة مع virtual
يعني: ربط هذه الطريقة باستخدام التشغيل نوع الكائن، وليس تجميع نوع الساعة (دينامية ملزمة).
ووسم طريقة virtual
الفئة الأساسية مع override
في فئة مشتقة يعني: هذا هو الأسلوب على الالتزام باستخدام التشغيل نوع الكائن (دينامية ملزمة).
ووسم طريقة الفئة الأساسية virtual
مع new
في فئة مشتقة يعني: هذا هو الأسلوب الجديد، وهذا لا علاقة له واحد يحمل نفس الاسم في الفئة الأساسية، وينبغي أن تكون ملزمة باستخدام نوع وقت الترجمة الكائن (ثابت ملزم ).
وليس بمناسبة طريقة virtual
الفئة الأساسية في فئة مشتقة يعني: ويتميز هذا الأسلوب كما new
(ثابت ملزم).
ووسم على abstract
طريقة يعني: هذا الأسلوب هو ظاهري، لكنني لن يعلن الجسم لذلك وفئتها أيضا مجردة (دينامية ملزمة).