Предотвратить вызов метода интерфейса, реализованного в базовом классе, в производном классе C#

Можно ли реализовать интерфейс в базовом классе и разрешить вызов/переопределение реализованного метода на первом уровне производного класса, но предотвратить его вызов из любых других производных классов?

    public interface IInterfaceSample
    {
        bool Test();
    }

    public class Base: IInterfaceSample
    {
        public virtual bool Test()
        {
             return True;
        }
    }

    public class Sub1: Base
    {
        //I need to be able to override the Test method here
        public override bool Test()
        {
             return True;
        }
    }

    //Under a separate project:
    public class Sub2: Sub1
    {
       //I need to prevent overriding the interface implementation in this class
    }

Теперь мне нужно это:

    var b = new Base();
    b.Test();//This should work

    var s1 = new Sub1();
    s1.Test();//I need this to work too

    var s2 = new Sub2();
    s2.Test();//I need to prevent doing this

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

В моем случае мне нужно, чтобы класс Sub2 имел доступ к свойствам в Sub1, но только это, и не имел доступа к методам этого класса и особенно к методам реализации интерфейса.

Единственный способ, которым я смог это сделать, — вообще не использовать интерфейсы и сделать это так:

    public class Base
    {
        internal virtual bool Test()
        {
             return True;
        }
    }

    public class Sub1: Base
    {
        //I am able to override the Test method here
        internal override bool Test()
        {
             return True;
        }
    }

    //Under a separate project:
    public class Sub2: Sub1
    {
       //Nothing to override here which is what i need
    }

    var b = new Base();
    b.Test();//This works

    var s1 = new Sub1();
    s1.Test();//This works too

    var s2 = new Sub2();
    s2.Test();//This is prevented

Однако мне интересно, доступно ли это по-прежнему для достижения с помощью интерфейсов, любая помощь очень ценится.


person Anas    schedule 19.03.2015    source источник
comment
Если Sub1 наследует интерфейс, то Sub2 не нужно наследовать какие-либо элементы интерфейса, поскольку они уже унаследованы через Sub1. Кстати, можно сделать интерфейс внутренним.   -  person Bauss    schedule 19.03.2015
comment
Мне кажется, что вам нужно перепроектировать свою объектную модель, если подкласс не должен иметь некоторые функции родительского класса, то должен ли он быть дочерним? что произойдет, если вы передадите экземпляр Sub2 методу, который может принять любой экземпляр Base?   -  person Trevor Pilley    schedule 19.03.2015
comment
Я только что попробовал это, однако Sub2 все еще может переопределить реализацию метода Test, а также метод Test можно вызвать из экземпляра класса Sub2, и мне нужно предотвратить оба из них.   -  person Anas    schedule 19.03.2015
comment
Хорошая мысль @TrevorPilley, однако, в моем случае единственная цель создания Sub2 в качестве дочернего элемента Sub1 — совместное использование структуры свойств без совместного использования других функций и методов, Sub1 и Sub2 будут использоваться в двух отдельных и разных концах проекта и ни один из них не будет доступен в неправильном месте, мне нужно только предотвратить дублирование Sub 1 в другом месте, просто удалив из него методы для достижения моей цели.   -  person Anas    schedule 19.03.2015


Ответы (3)


Нет, это невозможно — это нарушило бы всю суть полиморфизма. В частности, представьте, что вы не использовали var, а использовали типы явно:

Sub1 s2 = new Sub2();
s2.Test();

Это должно скомпилироваться:

  • Первая строка должна компилироваться, потому что Sub2 является производным от Sub1.
  • Вторая строка должна компилироваться, потому что вы хотели, чтобы s1.Test() скомпилировалось, где тип времени компиляции s1 также равен Sub1.

Как правило, если у вас есть два класса X и Y, и только некоторые общедоступные операции над X допустимы для Y, то Y не должен быть производным от X. Вы должны иметь возможность рассматривать любой экземпляр производного класса, как если бы он был экземпляром базового класса (и всех интерфейсов, которые он реализует).

person Jon Skeet    schedule 19.03.2015
comment
Спасибо @Jon, я понимаю вашу точку зрения, и это имеет смысл, вся цель того, что я пытаюсь сделать, - предотвратить дублирование структуры свойств и необходимость поддерживать ее снова и снова, я мог бы легко просто создать дублирующий класс с только свойства и использовать их, но позже это вызовет много головной боли, поэтому мне было интересно, есть ли способ сделать это с наследованием, но предотвратить использование методов. - person Anas; 19.03.2015
comment
@Anas: Честно говоря, я не совсем понимаю ваш комментарий о дублировании структуры свойств, но мне кажется, что вам, вероятно, следует использовать для этого композицию, а не наследование... - person Jon Skeet; 19.03.2015
comment
извините, если я недостаточно ясен, мне нужен класс со списком свойств в двух отдельных слоях проекта, например, открытый класс Class1 {public string P1 {get; set;} public string P2 {get; set;} public string P3 {get;set;} } Но этот класс имеет некоторые функции, которые должны быть доступны на одном уровне, но не на другом, поэтому я не хочу в конечном итоге дублировать класс на обоих концах с небольшими отличиями, поскольку он добавит накладные расходы на поддержание их обоих, чтобы синхронизировать их всякий раз, когда мы меняем свойство или что-то еще, было бы здорово, если бы я мог сделать это с наследованием. - person Anas; 19.03.2015
comment
@Anas: Похоже, вы должны выделить общую функциональность в отдельный тип - тогда вы можете использовать композицию, чтобы Class1 имел ссылку на CommonProperties, и то же самое для другого вашего класса. Наследование здесь не подходит. - person Jon Skeet; 19.03.2015
comment
Спасибо @Jon, что касается наследования, вы упомянули, что это неуместно, просто потому, что вы можете обойти его, явно используя типы, или есть какие-то другие недостатки, которые вы имеете в виду? - person Anas; 19.03.2015
comment
@Anas: Дело в том, что вы нарушаете предположение о наследовании: уместно использовать любой экземпляр производного типа в качестве экземпляра базового типа. Что-то, что имеет дело со ссылкой типа Base или Sub1, не должно беспокоиться о том, на самом деле это Sub2. - person Jon Skeet; 19.03.2015
comment
Спасибо @Jon за ваше разъяснение. - person Anas; 19.03.2015

Вы хотите, чтобы метод Test был доступен только в Sub1, но по-прежнему имел те же свойства, что и Sub2. Этого можно добиться, изменив цепочку наследования с этой:
введите здесь описание изображения
к этому:
введите здесь описание изображения

person B0Andrew    schedule 19.03.2015

Используйте sealed protected override bool Test() в Sub1

person Radin Gospodinov    schedule 19.03.2015
comment
Его по-прежнему можно будет вызывать из Sub2, но нельзя переопределить. - person Jamiec; 19.03.2015