новое слово в интерфейсах в c # - PullRequest
3 голосов
/ 27 августа 2009
using System;

namespace random

{

    interface IHelper
    {
        void HelpMeNow();
    }
    public class Base : IHelper
    {
        public void HelpMeNow()
        {
            Console.WriteLine("Base.HelpMeNow()");
        }
    }
    public class Derived : Base
    {
        public new void HelpMeNow()            ///this line
        {
            Console.WriteLine("Derived.HelpMeNow()");
        }
    }
    class Test
    {
        public static void Main()
        {
            Derived der = new Derived();
            der.HelpMeNow();
            IHelper helper = (IHelper)der;
            helper.HelpMeNow();
            Console.ReadLine();
        }
    }
}

новое ключевое слово в закомментированной строке меня немного смущает. Это означает, что оно переопределяет реализацию метода в базовом классе. Почему бы не использовать ключевое слово переопределения?

Ответы [ 3 ]

9 голосов
/ 27 августа 2009
0 голосов
/ 24 июля 2011

В общем, если интерфейс IBase реализует член DoSomething, а IDerived наследует / реализует Ibase, ожидается, что IDerived.DoSomething будет синонимом IBase.DoSomething. В общем, это полезно, так как избавляет разработчиков класса от необходимости предоставлять избыточные реализации. Однако в некоторых случаях производный интерфейс должен реализовывать элемент, имя которого совпадает с именем в базовом интерфейсе, но который должен быть реализован отдельно. Наиболее распространенные такие ситуации: (1) производный метод будет иметь тип возвращаемого значения, отличный от базового типа, или (2) базовый интерфейс (ы) реализует свойство (и) ReadOnly и / или WriteOnly с определенным и производным Тип должен реализовывать свойство Read-Write. По какой-то причине, если интерфейс IReadableFoo предоставляет свойство Foo только для чтения, IWritableFoo предоставляет свойство Foo только для записи, а интерфейс IMutableFoo просто наследует оба, компилятор не будет знать, ссылается ли ссылка на Foo на IReadableFoo.Foo или IWritableFoo. Foo. Несмотря на то, что только IReadableFoo.Foo может быть прочитан, и только IWritableFoo.Foo может быть записан, ни vb.net, ни C # не могут разрешить перегрузку, если не реализовано новое свойство чтения-записи Foo, которое обрабатывает оба.

0 голосов
/ 27 августа 2009

Это на самом деле не перекрывает, это затеняет это. При наличии ссылки на объект Derived функция Base HelpMeNow не будет доступна 1 , а derivedObject.HelpMeNow() вызовет реализацию Derived.

Это не то же самое, что переопределение виртуальной функции, которой не является HelpMeNow. Если объект Derived хранится в ссылке на Base или на IHelper, то будет вызываться Base *1016*, и реализация Derived будет недоступна.

Derived derivedReference = new Derived();
Base    baseReference    = derivedReference;
IHelper helperReference  = derivedReference;

derivedReference.HelpMeNow(); // outputs "Derived.HelpMeNow()"
baseReference.HelpMeNow();    // outputs "Base.HelpMeNow()"
helperReference.HelpMeNow();  // outputs "Base.HelpMeNow()"

Конечно, если вышеупомянутое не является желаемым поведением, и обычно это не так, есть две возможности. Если вы управляете Base, просто измените HelpMeNow() на виртуальный и переопределите его в Derived вместо его теневого копирования. Если вы не контролируете Base, то вы можете, по крайней мере, исправить это на полпути, переопределив IHelper, вот так:

class Derived : Base, IHelper{
    public new void HelpMeNow(){Console.WriteLine("Derived.HelpMeNow()");}

    void IHelper.HelpMeNow(){HelpMeNow();}
}

Эта версия Derived использует так называемую явную реализацию интерфейса , которая позволяет вам выполнить контракт на реализацию интерфейса без добавления реализации к общедоступному интерфейсу вашего класса. В этом примере у нас уже есть реализация в открытом интерфейсе Derived, которая унаследована от Base, поэтому мы должны явно реализовать IHelper, чтобы изменить ее 2 . В этом примере мы просто перенаправляем реализацию IHelper.HelpMeNow на наш открытый интерфейс, который является тенью Base.

Таким образом, с этим изменением вызов baseReference.HelpMeNow() по-прежнему выводит "Base.HelpMeNow ()", но вызов helperReference.HelpMeNow() теперь будет выводить "Derived.HelpMeNow ()". Не так хорошо, как изменение реализации Base на виртуальную, но так же хорошо, как мы получим, если не будем контролировать Base.

1 Исключение: он является доступным из методов Derived, но только при наличии base., как в base.HelpMeNow().
2 Обратите внимание, что мы также должны объявить IHelper как интерфейс, который реализует класс, даже если мы наследуем это объявление от Base.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...