C # лучшая частичная реализация интерфейса в базовом / абстрактном классе - PullRequest
5 голосов
/ 07 февраля 2012

.net не разрешает частичную реализацию интерфейса в базовых классах.В качестве смягчения я пришел к 3 альтернативным решениям.Пожалуйста, помогите мне решить, что является более универсальным с точки зрения рефакторинга, ошибок компиляции / выполнения, читабельности.Но сначала пара комментариев.

  • Конечно, вы всегда можете привести объект к IFoo и вызвать любой метод без предупреждения компилятора.Но это не логично, вы бы не сделали это нормально.Эта конструкция не возникнет в результате рефакторинга.
  • Я хочу максимальное разделение.Прямой контракт класса (публичные методы и свойства) должен быть отделен реализациями интерфейса.Я часто использую интерфейсы для разделения взаимодействий между объектами.

Мое сравнение:

  1. BaseClass1 / MyClass1:
    • con: Необходимо создать виртуальный реферат в BaseClass1для каждого не реализованного метода IFoo.
    • con: добавление дополнительного метода - небольшое влияние на производительность во время выполнения.
  2. BaseClass2 / MyClass2:
    • con: предупреждение компилятора отсутствует, если реализация Method2 отсутствуетв MyClass2.Исключение во время выполнения.Рефакторинг с плохим охватом модульных тестов может потенциально дестабилизировать код.
    • con: необходимо добавить дополнительную устаревшую конструкцию для предотвращения прямого вызова метода из дочерних классов.
    • con: Method2 является общедоступным для BaseClass1, поэтому он является частьюклассового контракта сейчас.Нужно поставить конструкцию «Устаревший», чтобы предотвратить прямой вызов, а не через IFoo.
  3. BaseClass3 / MyClass3:
    • pro: (по сравнению с # 2).Более читабельно.Вы видите, что MyClass2.Method2 является реализацией IFoo, а не просто каким-либо переопределенным методом.
public interface IFoo
{
    void Method1();
    void Method2();
}
public abstract class BaseClass1 : IFoo
{
    void IFoo.Method1()
    { 
        //some implementation
    }

    void IFoo.Method2()
    {
        IFooMethod2();
    }

    protected abstract void IFooMethod2();
}

public class MyClass1 : BaseClass1
{
    [Obsolete("Prohibited direct call from child classes. only inteface implementation")]
    protected override void IFooMethod2()
    {
        //some implementation
    }
}
public abstract class BaseClass2 : IFoo
{
    void IFoo.Method1()
    {
        //some implementation
    }

    [Obsolete("Prohibited direct call from child classes. only inteface implementation")]
    public virtual void Method2()
    {
        throw new NotSupportedException();
    }
}

public abstract class MyClass2 : BaseClass2
{
    public override void Method2()
    {
        //some implementation
    }
}
public abstract class BaseClass3 : IFoo
{
    void IFoo.Method1()
    {
        //some implementation
    }

    void IFoo.Method2()
    {
        throw new NotSupportedException();
    }
}

public abstract class MyClass3 : BaseClass3, IFoo
{
    void IFoo.Method2()
    {
        //some implementation
    }
}

Ответы [ 4 ]

9 голосов
/ 11 августа 2012

Мне нравится эта версия, базовый класс не может быть создан, потому что его абстракция, производный класс должны перечислить IFoo в своем объявлении, иначе он не будет реализовывать интерфейс, а затем он несет полную ответственность за реализацию остальныхинтерфейс.Единственный недостаток, который я вижу, это то, что вы не можете явно реализовать методы интерфейса в базовом классе (т. Е. Нет IFoo: Method1), но в противном случае это довольно низкая служебная версия.

public interface IFoo
{
    void Method1();
    void Method2();
}

public abstract class BaseClass1
{
    public void Method1()
    {
        //some implementation
    }
}

public class MyClass1 : BaseClass1, IFoo
{
    public void Method2()
    {
        //some implementation
    }
}
6 голосов
/ 07 февраля 2012

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

Вот некоторые общие решения

Плохое решение

  • Бросить исключение (NonImplementedException или NotSupportedException, см. пример )
  • Объявить его устаревшим (с самого начала хорошо его оформить)

Лучшее решение

  • Явная реализация интерфейса, но вы все еще реализуете ее (просто отчасти скрываете)

Лучшее решение

  • Использовать сегрегацию интерфейса (разделите ваш толстый интерфейс на более тонкие и более управляемые)
5 голосов
/ 07 февраля 2012

Хорошо, вы можете попробовать следующее, поскольку BaseClass является абстрактным:

public interface IFoo
{
    void Method1();

    void Method2();
}

public abstract class BaseClass : IFoo
{
    public void Method1()
    {
        // Common stuff for all BaseClassX classes
    }

    // Abstract method: it ensures IFoo is fully implemented
    // by all classes that inherit from BaseClass, but doesn't provide
    // any implementation right here.
    public abstract void Method2();
}

public class MyClass1 : BaseClass
{
    public override void Method2()
    {
        // Specific stuff for MyClass1
        Console.WriteLine("Class1");
    }
}

public class MyClass2 : BaseClass
{
    public override void Method2()
    {
        // Specific stuff for MyClass2
        Console.WriteLine("Class2");
    }
}

private static void Main(string[] args)
{
    IFoo test1 = new MyClass1();
    IFoo test2 = new MyClass2();

    test1.Method2();
    test2.Method2();

    Console.ReadKey();
}
0 голосов
/ 08 февраля 2012

Я бы предложил, чтобы абстрактный базовый класс реализовывал интерфейс с методами, которые вызывают методы protected abstract, как показано в вашем первом примере, за исключением методов, которые некоторые производные классы могут не реализовать (после «бросить все в * 1002»).* но не все методы на самом деле работают (шаблон);это могут быть protected virtual заглушки, которые выбрасывают NotSupportedException.

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

Правильный шаблон в VB.net будет выглядеть примерно так: MustOverride Sub IFoo_Method1() Implements IFoo.Method1, что позволит избежать дополнительных затрат на вызов функции, но C # не предоставляет никаких средств для реализации интерфейса с защищенным членом.,Использование явной реализации интерфейса для любого метода, который, возможно, придется переопределить в дочернем классе, несколько странно, поскольку повторная реализация дочерним интерфейсом дочернего элемента невозможна для цепочки реализации родительского элемента.

...