it-swarm.dev

لماذا استدعاء أسلوب في صفي المشتق استدعاء الأسلوب الفئة الأساسية؟

النظر في هذا الرمز:

class Program
{
    static void Main(string[] args)
    {
        Person person = new Teacher();
        person.ShowInfo();
        Console.ReadLine();
    }
}

public class Person
{
    public void ShowInfo()
    {
        Console.WriteLine("I am Person");
    }
}
public class Teacher : Person
{
    public new void ShowInfo()
    {
        Console.WriteLine("I am Teacher");
    }
}

عند تشغيل هذا الرمز ، يتم إنتاج ما يلي:

أنا شخص

ومع ذلك ، يمكنك أن ترى أنه مثيل لـ Teacher وليس من Person. لماذا رمز تفعل ذلك؟

143
user1968030

هناك فرق بين new و virtual/override.

يمكنك أن تتخيل أن هذه الفئة ، عندما يتم إنشاء مثيل لها ، ليست أكثر من مجرد جدول مؤشرات ، تشير إلى التنفيذ الفعلي لأساليبها. يجب أن تظهر الصورة التالية بشكل جيد:

Illustration of method implementations

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

1. دروس مجردة

أول واحد هو abstract. أساليب abstract تشير ببساطة إلى أي مكان:

Illustration of abstract classes

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

public abstract class Person
{
    public abstract void ShowInfo();
}

public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am a teacher!");
    }
}

public class Student : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am a student!");
    }
}

في حالة الاتصال ، يختلف سلوك ShowInfo ، بناءً على التطبيق:

Person person = new Teacher();
person.ShowInfo();    // Shows 'I am a teacher!'

person = new Student();
person.ShowInfo();    // Shows 'I am a student!'

كلاهما ، Students و Teachers هما Persons ، لكنهما يتصرفان بشكل مختلف عندما يُطلب إليهما المطالبة بمعلومات عن أنفسهم. ومع ذلك ، فإن طريقة مطالبتهم بمطالبة معلوماتهم ، هي نفسها: استخدام واجهة فئة Person.

إذن ماذا يحدث وراء الكواليس ، عندما ترث من Person؟ عند تطبيق ShowInfo ، لا يشير المؤشر إلى في أي مكان بعد الآن ، يشير الآن إلى التنفيذ الفعلي! عند إنشاء مثيل Student ، يشير إلى Students ShowInfo:

Illustration of inherited methods

2. الأساليب الافتراضية

الطريقة الثانية هي استخدام أساليب virtual. السلوك هو نفسه ، إلا أنك تقدم اختياري تطبيق افتراضي في الفصل الأساسي. يمكن تثبيت الفئات التي تضم أعضاء virtual ، ولكن يمكن أن توفر الفئات الموروثة تطبيقات مختلفة. إليك ما يجب أن يبدو عليه الرمز في الواقع:

public class Person
{
    public virtual void ShowInfo()
    {
        Console.WriteLine("I am a person!");
    }
}

public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am a teacher!");
    }
}

الفرق الرئيسي هو أن العضو الأساسي Person.ShowInfo لا يشير إلى في أي مكان / بعد الآن. هذا هو السبب أيضًا ، لماذا يمكنك إنشاء مثيلات لـ Person (وبالتالي لا يلزم وضع علامة عليها كـ abstract بعد الآن):

Illustration of a virtual member inside a base class

يجب أن تلاحظ أن هذا لا يبدو مختلفًا عن الصورة الأولى في الوقت الحالي. هذا لأن الأسلوب virtual يشير إلى تطبيق " الطريقة القياسية ". باستخدام virtual ، يمكنك إخبار Persons ، أنه يمكن (لا يجب ) توفير تطبيق مختلف لـ ShowInfo. إذا قدمت تطبيقًا مختلفًا (باستخدام override) ، مثلما فعلت بالنسبة لـ Teacher أعلاه ، فستظهر الصورة كما هي لـ abstract. تخيل أننا لم نوفر تطبيقًا مخصصًا لـ Students:

public class Student : Person
{
}

يطلق على الكود مثل هذا:

Person person = new Teacher();
person.ShowInfo();    // Shows 'I am a teacher!'

person = new Student();
person.ShowInfo();    // Shows 'I am a person!'

وستظهر صورة Student بالشكل التالي:

Illustration of the default implementation of a method, using virtual-keyword

3. الكلمة السحرية "الجديدة" المعروفة باسم "التظليل"

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

Illustration of the "way around" using the new-keyword

تنفيذ يشبه واحد ، التي قدمتها. يختلف السلوك ، استنادًا إلى طريقة وصولك إلى الطريقة:

Teacher teacher = new Teacher();
Person person = (Person)teacher;

teacher.ShowInfo();    // Prints 'I am a teacher!'
person.ShowInfo();     // Prints 'I am a person!'

يمكن أن يكون هذا السلوك مطلوبًا ، لكن في قضيتك يكون مضللاً.

آمل أن يجعل هذا الأمور أكثر وضوحًا لفهمها لك!

364
Carsten

يستخدم تعدد الأشكال في C # الواقعية الصريحة ، على غرار C++ ولكن على عكس Java. هذا يعني أنه يجب عليك صراحةً تحديد الطرق على أنها قابلة للتحجيم (أي virtual). في C # ، يتعين عليك أيضًا وضع علامة صريحة على أساليب التجاوز كطريقة تجاوز (أي override) لمنع الأخطاء المطبعية.

public class Person
{
    public virtual void ShowInfo()
    {
        Console.WriteLine("I am Person");
    }
}

public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am Teacher");
    }
}

في الكود في سؤالك ، يمكنك استخدام new ، والذي يستخدم shadowing بدلاً من التجاوز. يؤثر التظليل فقط على دلالات وقت الترجمة بدلاً من دلالات وقت التشغيل ، وبالتالي الإخراج غير المقصود.

45
user142019

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

public class Person
{
    public virtual void ShowInfo()
    {
        Console.WriteLine("I am Person");
    }
}
public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am Teacher");
    }
}

طرق افتراضية

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

باستخدام جديد للتظليل

أنت تستخدم مفتاحًا جديدًا Word بدلاً من التجاوز ، وهذا ما يفعله الجديد

  • إذا لم تسبق الطريقة في الفئة المشتقة بكلمات رئيسية جديدة أو تجاوزها ، فسيصدر المحول البرمجي تحذيرًا وستتصرف الطريقة كما لو كانت الكلمة الرئيسية الجديدة موجودة.

  • إذا كانت {الطريقة في الفئة المشتقة مسبوقة بالكلمة الرئيسية الجديدة ، فسيتم تعريف الطريقة على أنها مستقلة عن الطريقة في الفئة الأساسية} ، هذا المقال MSDN يشرحها جيدًا.

الربط المبكر مقابل الربط المتأخر

لدينا ارتباط مبكر في وقت التحويل البرمجي للطريقة العادية (غير الافتراضية) وهي حالة currrent - {سيقوم المحول البرمجي بربط استدعاء طريقة الفئة الأساسية التي هي طريقة نوع المرجع (الفئة الأساسية) بدلاً من الكائن في حكم الفئة الأساسية أي كائن فئة مشتق. هذا لأن ShowInfo ليست طريقة افتراضية. يتم تنفيذ الربط المتأخر في وقت التشغيل لـ (طريقة افتراضية/متجاوزة) باستخدام طريقة الأسلوب الظاهري (vtable).

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

لكائن يحتوي على أي أساليب افتراضية ، سيقوم المترجم بإنشاء جدول v. هذا هو في الأساس مجموعة تحتوي على عناوين الطرق الافتراضية. سيحتوي كل كائن يحتوي على طريقة افتراضية على عضو مخفي تم إنشاؤه بواسطة برنامج التحويل البرمجي وهو عنوان الجدول v. عندما يتم استدعاء وظيفة افتراضية ، سوف يعمل المترجم على تحديد موضع الأسلوب المناسب في الجدول v. سيقوم بعد ذلك بإنشاء رمز للبحث في الكائنات v-table واستدعاء الأسلوب الظاهري في هذا الموضع ، Reference .

25
Adil

أريد أن أبني على إجابة أختر . للتأكد من الاكتمال ، يتمثل الفرق في أن OP يتوقع الكلمة الأساسية newفي طريقة الفئة المشتقة لتجاوز طريقة الفئة الأساسية. ما تقوم به بالفعل هو إخفاءطريقة الفئة الأساسية.

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

هذا يختلف عن Java ، والذي يسمح "بتجاوزات ضمنية" ؛ على سبيل المثال ، الطرق (غير الثابتة) ، يؤدي تحديد طريقة من نفس التوقيع (اسم ورقم/نوع المعلمات) إلى تجاوز الطبقة الفرعية الطبقة العليا.

نظرًا لأنه من المفيد غالبًا توسيع وظيفة الطريقة غير الظاهرية التي لا تتحكم فيها أو تجاوزها ، فإن C # يتضمن أيضًا الكلمة الأساسية للسياق newname__. الكلمة الأساسية new"تخفي" الطريقة الأصل بدلاً من تجاوزها. يمكن إخفاء أي طريقة قابلة للتوريث سواء كانت افتراضية أم لا ؛ يتيح لك هذا ، المطور ، الاستفادة من الأعضاء الذين تريد أن ترثهم من أحد الوالدين ، دون الاضطرار إلى التغلب على الأشخاص الذين لا تريدهم ، مع السماح لك بتقديم "الواجهة" نفسها للمستهلكين من الكود.

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

بالإضافة إلى ذلك ، لن تقوم الكلمة الأساسية newبهذا فقط صراحة ، C # تسمح بإخفاء الطريقة الضمنية ؛ ما عليك سوى إخفاء طريقة لها نفس التوقيع كطريقة لفئة الأصل ، بدون overrideأو new، وستخفيها (على الرغم من أنها ستنتج تحذيرًا مترجمًا أو شكوى من بعض مساعدي إعادة التسكين مثل ReSharper أو CodeRush). هذا هو الحل الوسط الذي توصل إليه مصممو C # بين التجاوزات الصريحة لـ C++ مقابل التفضيلات الضمنية لـ Java ، وفي حين أنها أنيقة ، فإنها لا تنتج دائمًا السلوك الذي تتوقعه إذا كنت من خلفية في أي من اللغات القديمة.

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

class Foo { public virtual void DoFoo() { Console.WriteLine("Foo"); } }
class Bar:Foo { public override sealed void DoFoo() { Console.WriteLine("Bar"); } }
class Baz:Bar { public virtual void DoFoo() { Console.WriteLine("Baz"); } }
class Bai:Baz { public override void DoFoo() { Console.WriteLine("Bai"); } }
class Bat:Bai { public new void DoFoo() { Console.WriteLine("Bat"); } }
class Bak:Bat { }

Foo foo = new Foo();
Bar bar = new Bar();
Baz baz = new Baz();
Bai bai = new Bai();
Bat bat = new Bat();

foo.DoFoo();
bar.DoFoo();
baz.DoFoo();
bai.DoFoo();
bat.DoFoo();

Console.WriteLine("---");

Foo foo2 = bar;
Bar bar2 = baz;
Baz baz2 = bai;
Bai bai2 = bat;
Bat bat2 = new Bak();

foo2.DoFoo();
bar2.DoFoo();
baz2.DoFoo();
bai2.DoFoo();    

Console.WriteLine("---");

Foo foo3 = bak;
Bar bar3 = bak;
Baz baz3 = bak;
Bai bai3 = bak;
Bat bat3 = bak;

foo3.DoFoo();
bar3.DoFoo();
baz3.DoFoo();
bai3.DoFoo();    
bat3.DoFoo();

انتاج:

Foo
Bar
Baz
Bai
Bat
---
Bar
Bar
Bai
Bai
Bat
---
Bar
Bar
Bai
Bai
Bat

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

المجموعة الثانية من خمسة هي نتيجة تعيين كل مثيل إلى متغير من النوع الأصل الفوري. الآن ، بعض الاختلافات في السلوك تهتز. foo2 ، والذي هو في الواقع Barالمدلى بها كـ Foo، سيظل يجد الطريقة المشتقة من نوع الكائن الفعلي Bar. bar2 هو Baz، ولكن على عكس foo2 ، لأن Baz لا يتجاهل تطبيق Bar بشكل صريح (لا يمكن ؛ Bar sealedعليه) ، فلن تتم مشاهدته في وقت التشغيل عند البحث "من أعلى لأسفل" ، لذلك يتم استدعاء تطبيق Bar بدلاً منه . لاحظ أنه لا يتعين على Baz استخدام الكلمة الأساسية new؛ ستحصل على تحذير برنامج التحويل البرمجي إذا حذفت الكلمة الأساسية ، ولكن السلوك الضمني في C # هو إخفاء الطريقة الأصل. baz2 هو Bai، الذي يتخطى تطبيق Bazname __'s new، لذلك سلوكه يشبه foo2. يسمى تطبيق نوع الكائن الفعلي في باي. bai2 هو Bat، الذي يخفي مرة أخرى تطبيق الأسلوب الأصل Bainame __ ، ويتصرف بنفس طريقة bar2 على الرغم من أن تطبيق Bai لم يكن مختومًا ، لذلك من الناحية النظرية قد يكون قد تم تجاوزه بدلاً من إخفاء الطريقة. أخيرًا ، bat2 هو Bak، الذي لا يوجد لديه تطبيق غالب من أي نوع ، ويستخدم ببساطة تطبيق الأصل.

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

7
KeithS

يرجى قراءة حول تعدد الأشكال في C #: تعدد الأشكال (C # Programming Guide)

هذا مثال من هناك:

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

DerivedClass B = new DerivedClass();
B.DoWork();  // Calls the new method.

BaseClass A = (BaseClass)B;
A.DoWork();  // Calls the old method.
4
Emil

تحتاج إلى جعلها virtual ثم تجاوز تلك الوظيفة في Teacher. بما أنك ترث المؤشر الأساسي وتستخدمه للإشارة إلى فئة مشتقة ، فأنت بحاجة إلى تجاوزه باستخدام virtual. new لإخفاء طريقة الفئة base على مرجع فئة مشتقة وليس مرجع فئة base.

3
Jay Patel

أود أن أضيف بضعة أمثلة أخرى للتوسع في المعلومات حول هذا. آمل أن يساعد هذا أيضًا:

فيما يلي نموذج للكود يقوم بمسح الهواء حول ما يحدث عندما يتم تعيين نوع مشتق لنوع أساسي. ما هي الأساليب المتاحة والفرق بين الأساليب التي تم تجاوزها والخفية في هذا السياق.

namespace TestApp
{
    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            a.foo();        // A.foo()
            a.foo2();       // A.foo2()

            a = new B();    
            a.foo();        // B.foo()
            a.foo2();       // A.foo2()
            //a.novel() is not available here

            a = new C();
            a.foo();        // C.foo()
            a.foo2();       // A.foo2()

            B b1 = (B)a;    
            b1.foo();       // C.foo()
            b1.foo2();      // B.foo2()
            b1.novel();     // B.novel()

            Console.ReadLine();
        }
    }


    class A
    {
        public virtual void foo()
        {
            Console.WriteLine("A.foo()");
        }

        public void foo2()
        {
            Console.WriteLine("A.foo2()");
        }
    }

    class B : A
    {
        public override void foo()
        {
            // This is an override
            Console.WriteLine("B.foo()");
        }

        public new void foo2()      // Using the 'new' keyword doesn't make a difference
        {
            Console.WriteLine("B.foo2()");
        }

        public void novel()
        {
            Console.WriteLine("B.novel()");
        }
    }

    class C : B
    {
        public override void foo()
        {
            Console.WriteLine("C.foo()");
        }

        public new void foo2()
        {
            Console.WriteLine("C.foo2()");
        }
    }
}

الشذوذ القليل الآخر هو أنه بالنسبة لسطر التعليمات البرمجية التالي:

A a = new B();    
a.foo(); 

سيُظهر برنامج التحويل البرمجي VS (intellisense) a.foo () كـ A.foo ().

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

يجب أن يساعد نموذج التعليمات البرمجية هذا في تحديد هذه المحاذير!

3
Vaibhav

تخبر new الكلمة الأساسية أن الطريقة في الفصل الحالي لن تعمل إلا إذا كان لديك مثيل لفئة المعلم المخزنة في متغير من نوع المعلم. أو يمكنك تشغيله باستخدام المسبوكات: ((معلم) شخص). ShowInfo ()

2
Miguel

يختلف C # عن Java في سلوك تجاوز فئة الوالد/الطفل. تبعًا للإعدادات الافتراضية في Java ، تكون كل الطرق افتراضية ، لذلك يتم دعم السلوك الذي تريده خارج الصندوق.

في C # ، يجب عليك وضع علامة على طريقة ما كأنها افتراضية في الفئة الأساسية ، ثم ستحصل على ما تريد.

2
Adrian Salazar

قد يكون الأوان قد فات ... لكن السؤال بسيط ويجب أن يكون للإجابة نفس المستوى من التعقيد.

في المتغير الخاص بك ، لا يعرف الشخص أي شيء عن Teacher.ShowInfo (). لا توجد طريقة لاستدعاء الطريقة الأخيرة من مرجع الفئة الأساسية ، لأنها ليست افتراضية.

هناك طريقة مفيدة للميراث - حاول أن تتخيل ما الذي تريد قوله مع التسلسل الهرمي للشفرة. حاول أيضًا أن تتخيل ماذا تقول أداة أو أداة أخرى عن نفسها. مثلا إذا قمت بإضافة وظيفة افتراضية إلى فئة أساسية افترض: 1. يمكن أن يكون لها تطبيق افتراضي ؛ 2. قد يتم تنفيذه في فئة مشتقة. إذا قمت بإضافة دالة مجردة ، فهذا يعني شيئًا واحدًا فقط - يجب إنشاء فئة فرعية للتطبيق. ولكن في حال كان لديك وظيفة واضحة - لا تتوقع أن يغير أي شخص تنفيذها.

1
Illia Levandovskyi

نوع المتغير 'المعلم' هنا هو typeof(Person) وهذا النوع لا يعرف أي شيء عن فئة المعلم ولا يحاول البحث عن أي طرق في الأنواع المشتقة. للاتصال بأسلوب صف المعلم ، يجب عليك إرسال المتغير الخاص بك: (person as Teacher).ShowInfo().

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

public class Program
{
    private static void Main(string[] args)
    {
        Person teacher = new Teacher();
        teacher.ShowInfo();

        Person incognito = new IncognitoPerson ();
        incognito.ShowInfo();

        Console.ReadLine();
    }
}

public class Person
{
    public virtual void ShowInfo()
    {
        Console.WriteLine("I am Person");
    }
}

public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am Teacher");
    }
}

public class IncognitoPerson : Person
{

}
1
Vi Ki

بناءً على العرض التوضيحي الممتاز الذي قدمه Keith S. وإجابات كل فرد على الجودة ومن أجل اكتمال uber ، يمكنك المضي قدمًا وإرساء تطبيقات واجهة صريحة لإظهار كيفية عمل ذلك. النظر في أدناه:

مساحة الاسم LinqConsoleApp {

class Program
{

    static void Main(string[] args)
    {


        Person person = new Teacher();
        Console.Write(GetMemberName(() => person) + ": ");
        person.ShowInfo();

        Teacher teacher = new Teacher();
        Console.Write(GetMemberName(() => teacher) + ": ");
        teacher.ShowInfo();

        IPerson person1 = new Teacher();
        Console.Write(GetMemberName(() => person1) + ": ");
        person1.ShowInfo();

        IPerson person2 = (IPerson)teacher;
        Console.Write(GetMemberName(() => person2) + ": ");
        person2.ShowInfo();

        Teacher teacher1 = (Teacher)person1;
        Console.Write(GetMemberName(() => teacher1) + ": ");
        teacher1.ShowInfo();

        Person person4 = new Person();
        Console.Write(GetMemberName(() => person4) + ": ");
        person4.ShowInfo();

        IPerson person3 = new Person();
        Console.Write(GetMemberName(() => person3) + ": ");
        person3.ShowInfo();

        Console.WriteLine();

        Console.ReadLine();

    }

    private static string GetMemberName<T>(Expression<Func<T>> memberExpression)
    {
        MemberExpression expressionBody = (MemberExpression)memberExpression.Body;
        return expressionBody.Member.Name;
    }

}
interface IPerson
{
    void ShowInfo();
}
public class Person : IPerson
{
    public void ShowInfo()
    {
        Console.WriteLine("I am Person == " + this.GetType());
    }
    void IPerson.ShowInfo()
    {
        Console.WriteLine("I am interface Person == " + this.GetType());
    }
}
public class Teacher : Person, IPerson
{
    public void ShowInfo()
    {
        Console.WriteLine("I am Teacher == " + this.GetType());
    }
}

}

وهنا الإخراج:

الشخص: أنا شخص == LinqConsoleApp.Teacher

المعلم: أنا مدرس == LinqConsoleApp.Teacher

person1: أنا مدرس == LinqConsoleApp.Teacher

person2: أنا مدرس == LinqConsoleApp.Teacher

teacher1: أنا مدرس == LinqConsoleApp.Teacher

person4: أنا شخص == LinqConsoleApp.Person

person3: أنا واجهة الشخص == LinqConsoleApp.Person

شيئين ملاحظة:
أسلوب Teacher.ShowInfo () يحذف الكلمة الأساسية الجديدة. عندما يتم حذف الجديد ، يكون سلوك الطريقة هو نفسه كما لو تم تعريف الكلمة الأساسية الجديدة بشكل صريح.

يمكنك فقط استخدام الكلمة الأساسية المتجاوزة بالاقتران مع المفتاح الظاهري Word. يجب أن تكون طريقة الفئة الأساسية ظاهرية. أو مجردة في هذه الحالة يجب أن يكون الفصل مجردة أيضًا.

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

يحصل المعلم على تطبيق المعلم المستمد من ShowInfo لأن المعلم هو Typeof (المعلم) وليس على مستوى وراثة الشخص.

يحصل person1 على تطبيق المعلم المشتق لأنه .GetType (المعلم) والكلمة الرئيسية الجديدة الضمنية تخفي تطبيق الأساس.

يحصل person2 أيضًا على تطبيق المعلم المشتق على الرغم من أنه يقوم بتنفيذ IPerson ويحصل على ممثلون صريحون لـ IPerson. هذا مرة أخرى لأن فئة المعلم لا يقوم بتطبيق أسلوب IPerson.ShowInfo () بشكل صريح.

teacher1 يحصل أيضًا على تطبيق Teacher المشتق لأنه .GetType (Teacher).

يحصل person3 فقط على تطبيق IPerson لـ ShowInfo لأن فئة الشخص هي فقط التي تطبق الطريقة بشكل صريح و person3 هو مثيل لنوع IPerson.

من أجل تنفيذ واجهة بشكل صريح ، يجب أن تعلن عن مثيل var من نوع الواجهة المستهدفة ويجب أن تنفذ فئة (مؤهلاً بالكامل) عضو (أعضاء) الواجهة بشكل كامل.

لاحظ أنه حتى person4 يحصل على تطبيق IPerson.ShowInfo. هذا لأنه على الرغم من أن person4 هو .GetType (Person) وعلى الرغم من أن الشخص ينفذ IPerson ، إلا أن person4 ليس مثيلًا لـ IPerson.

0
steely

أردت فقط إعطاء إجابة موجزة -

يجب عليك استخدام virtual و override في الفصول التي يمكن تجاوزها. استخدم virtual للأساليب التي يمكن تجاوزها بواسطة الفئات الفرعية واستخدام override للأساليب التي يجب أن تتجاوز هذه الأساليب virtual.

0
Shruti Kapoor

يقوم المترجم بهذا لأنه لا يعرف أنه Teacher. كل ما يعرفه هو أنه Person أو شيء مشتق منه. كل ما يمكن القيام به هو استدعاء الأسلوب Person.ShowInfo().

0
Cole Johnson

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

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

public class inheritance{

    public static void main(String[] args){

        Person person = new Teacher();
        person.ShowInfo();
    }
}

class Person{

    public void ShowInfo(){
        System.out.println("I am Person");
    }
}

class Teacher extends Person{

    public void ShowInfo(){
        System.out.println("I am Teacher");
    }
}
0
Sanjeev Chauhan

عينة LinQPad لإطلاق العمياء وتقليل ازدواجية التعليمات البرمجية التي أعتقد أنه ما كنت تحاول القيام به.

void Main()
{
    IEngineAction Test1 = new Test1Action();
    IEngineAction Test2 = new Test2Action();
    Test1.Execute("Test1");
    Test2.Execute("Test2");
}

public interface IEngineAction
{
    void Execute(string Parameter);
}

public abstract class EngineAction : IEngineAction
{
    protected abstract void PerformAction();
    protected string ForChildren;
    public void Execute(string Parameter)
    {  // Pretend this method encapsulates a 
       // lot of code you don't want to duplicate 
      ForChildren = Parameter;
      PerformAction();
    }
}

public class Test1Action : EngineAction
{
    protected override void PerformAction()
    {
        ("Performed: " + ForChildren).Dump();
    }
}

public class Test2Action : EngineAction
{
    protected override void PerformAction()
    {
        ("Actioned: " + ForChildren).Dump();
    }
}
0
Yrd