C #: интерфейс и аннотация для включения реализации и переопределения - PullRequest
2 голосов
/ 19 мая 2011

Мне нужно спроектировать около 5 разных классов.

Многие части этих классов будут очень похожи, но они будут иметь небольшие различия (конечно).

Если я напишу interface, который реализует каждый из моих разработанных классов, то каждый abstract метод в interface должен быть полностью написан.

Если я напишу класс base, из которого происходит каждый из моих разработанных классов, то каждый класс автоматически получит некоторую функциональность в методе virtual, который я создаю в классе base, но все они должны будут быть переопределенным, вызывая функциональность base, чтобы включить небольшие различия.

Есть ли способ объединить эти функции? Мне нравится требовать реализации методов interface, но мне также нравится задавать функции, предварительно запрограммированные классом base.

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

РЕДАКТИРОВАТЬ: Итак, учитывая это:

abstract class Base
{
    virtual protected void OptionallyOverridable() {}
    abstract protected void SubclassMustImplement();
}

... есть ли способ написать что-то вроде этого:

abstract class Base2
{
    DataEventType EventType;
    DataChangedEventHandler OnDataChange;

    virtual protected void OptionallyOverridable() {}
    abstract protected void SubclassMustImplement() {
      // values here are guaranteed
    }
}

Ответы [ 5 ]

5 голосов
/ 19 мая 2011

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

public interface IFoo
{
    public void Bar();
    public bool Baz();
}

public abstract class BaseFoo : IFoo
{
    //fields/properties used in all classes
    public void Bar()
    { //Implementation }

    public bool Baz()
    { //Implementation }
}

public class DerivedFoo : BaseFoo, IFoo {...}
3 голосов
/ 20 мая 2011

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

Определите ваш интерфейс ...

public interface IFoo
{
    void DoIt();
    void DoItWithoutDefaultBehavior();
}

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

public abstract class BaseFoo : IFoo
{
    public virtual void DoIt()
    {
         // Base behavior
         Console.WriteLine("base");
    }

    // This one has no base behavior
    public abstract void DoItWithoutDefaultBehavior();
}

Некоторые из ваших подклассов могут использовать эти поведения по умолчанию ...

public class DerivedFoo1 : BaseFoo, IFoo
{
    // Doesn't override DoIt, takes BaseFoo

    public override void DoItWithoutDefaultBehavior()
    {
        Console.WriteLine("foo1");
    }
}

Другие могут добавить к ним ...

public class DerivedFoo2 : BaseFoo, IFoo
{
    public override void DoIt()
    {
        base.DoIt();
        // Additional stuff
        Console.WriteLine("derived");
    }

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

Вы можете вызывать методы через класс или интерфейс.

void Main()
{
    var foo1 = new DerivedFoo1();
    foo1.DoIt();

    var foo2 = new DerivedFoo2();
    foo2.DoIt();

    IFoo foo1AsFoo = new DerivedFoo1();
    foo1AsFoo.DoIt();

    IFoo foo2AsFoo = new DerivedFoo2();
    foo2AsFoo.DoIt();
}

См. Также: Пост Эрика Липперта , в котором обсуждается, должны ли DerivedFoo1 и DerivedFoo2 подтвердить, что они реализуют IFoo, когда BaseFoo уже говорит об этом. Они делают в моем примере ради явного.

EDIT Определенно можно добавлять события в интерфейс. Я внес следующие изменения в свой пример. Вы можете внести аналогичные изменения:

  • К interface IFoo, добавьте event EventHandler TheEvent;
  • К BaseFoo, добавьте public event EventHandler TheEvent;
  • К BaseFoo.DoIt, добавьте if (TheEvent != null) { var args = new EventArgs(); TheEvent(this, args); }
  • Добавить public void TheHandler(object sender, EventArgs e) { Console.WriteLine("Fired."); }
  • К Main, добавьте foo1.TheEvent += TheHandler;

Вывод теперь становится

base
Fired.
base
derived
base
base
derived
1 голос
/ 20 мая 2011

Вы можете смешивать как интерфейс, так и абстракцию, я думаю, что вы хотите что-то вроде этого

public interface IFoo
{
    void ImplementMe();
    int ImplementId { get; }
}

public abstract class BaseFoo : IFoo
{
    public virtual void ImplementMe()
    {
        CommonStuff();
    }

    public abstract int ImplementId { get; }

    private void CommonStuff()
    {
        // ... do stuff ?
    }
}

public sealed class FooImplementationA : BaseFoo
{
    public override int ImplementId { get { return 0; } }

    public override void ImplementMe()
    {
        MoreStuff();
        base.CommonStuff();
    }

    private void MoreStuff()
    {
        // ... do more stuff.
    }
}

public sealed class FooImplementationB : BaseFoo
{
    public override int ImplementId { get { return 1; } }

    public override void ImplementMe()
    {
        DifferentStuff();
        base.CommonStuff();
    }

    private void DifferentStuff()
    {
        // ... do different stuff.
    }
}

, но опять же вам не очень нужен интерфейс и вы можете просто получитьпокончить с использованием BaseFoo абстрактного класса

1 голос
/ 19 мая 2011

Да, и вы уже ответили сами.Вы сделаете оба:

  • Иметь базовый класс с базовой реализацией
  • Иметь дочерние классы (или базовый класс, почему бы и нет?) Реализовывать нужные интерфейсы.
0 голосов
/ 19 мая 2011

Ключевое слово "abstract" - это то, что вы ищете.

    abstract class Base
    {
        virtual protected void SubclassCanCallAndOptionallyOverride() {}
        abstract protected void SubclassMustBeImplement();
    }

Для более подробной информации: http://msdn.microsoft.com/en-us/library/ms173150.aspx

...