Свободные интерфейсы и множественное наследование в C #

Этот вопрос похож на этот . Разница в том, что я хотел бы иметь два базовых класса.

Пример:

public class Circle
{
    private string _radius { get; set; }

    public Circle Radius(string radius)
    {
        _radius = radius;
        return this;
    }
}

public class Box
{
    private string _width { get; set; }

    public Circle Width(string width)
    {
        _width = width;
        return this;
    }
}

public class CircleAndBox : Circle, Box // Can't do in c#
{
    // should contain methods from both Circle and Box, but return CircleAndBox
}

Возможно, Circle and Box был не лучшим примером. В основном они представляют собой классы с разными свойствами и методами. Класс CircleAndBox просто имеет те же свойства и методы, что и Circle и Box. CircleAndBox может иметь дополнительные свойства и методы, которых нет ни в Circle, ни в Box.

Желаемый результат

Я должен уметь писать:

var circle = new Circle().Radius("5");
var box = new Box().Width("6");
var circleAndBox = new CircleAndBox().Radius("5").Width("6");

Было бы здорово, если бы:

Когда я добавляю метод в класс Circle или Box, я не должен касаться класса CircleAndBox. Как и при обычном наследовании от одного класса, CircleAndBox должен автоматически наследовать все общедоступные методы как от Circle, так и от Box


person Dmitry Efimenko    schedule 02.08.2013    source источник
comment
Интерфейсы интерфейсы интерфейсы ... Зачем вам оба? Что такое коробка Circle +? Если это круг в коробке, то радиус круга составляет половину ширины коробки?   -  person Sayse    schedule 03.08.2013
comment
.Width("6") ?? а что с .Width("BigFat") ??   -  person Federico Berasategui    schedule 03.08.2013
comment
new CircleAndBox().Radius("5").Width("6"); Это так бегло, что я даже не понимаю, что это значит.   -  person spender    schedule 03.08.2013
comment
@spender сработает, если Radius вернет CircleAndBox: P (сомневаюсь!)   -  person Sayse    schedule 03.08.2013
comment
Вы неправильно сделали уроки? т.е. Box : Shape и Circle : Shape с общими методами в Shape?   -  person Sayse    schedule 03.08.2013
comment
CircleAndBox звучит как фантастическое нарушение принципа замены Лискова.   -  person David    schedule 03.08.2013
comment
лол, столько азарта! Возможно, Circle и Box были не лучшим примером. В основном они представляют собой классы с разными свойствами и методами. Класс CircleAndBox просто имеет те же свойства и методы, что и Circle и Box. CircleAndBox может иметь дополнительные свойства и методы, которых нет ни в Circle, ни в Box. Я добавлю это в описание вопроса   -  person Dmitry Efimenko    schedule 03.08.2013
comment
по поводу .Width (6) ... неважно, люди! Это всего лишь пример!   -  person Dmitry Efimenko    schedule 03.08.2013


Ответы (4)


CircleAndBox наследовать ни от одного класса, а скорее ссылаться на объекты этих классов. Придется переопределить методы каждого класса. Вы можете добавить неявные преобразования к Circle и Box, чтобы разрешить его использование в контекстах, где ожидаются ссылки на эти объекты.

public class CircleAndBox
{
    public Circle Circle { get; private set; }
    public Box Box { get; private set; }

    public CircleAndBox()
    {
        Circle = new Circle();
        Box = new Box();
    }

    public CircleAndBox Radius(string radius)
    {
        Circle.Radius(radius);
        return this;
    }

    public CircleAndBox Width(string width)
    {
        Box.Width(width);
        return this;
    }

    public static implicit operator Circle(CircleAndBox self)
    {
        return self == null ? null : self.Circle;
    }

    public static implicit operator Box(CircleAndBox self)
    {
        return self == null ? null : self.Box;
    }
}

Обратите внимание, что неявные преобразования не сохранят тип объекта, поэтому не следует использовать эту технику для передачи CircleAndBox методу, принимающему Box, и ожидать, что результат на другой стороне будет CircleAndBox.

CircleAndBox cb = new CircleAndBox();

// Implicit conversion, b contains a Box object.
Box b = cb;

// Compile-time error CS0030.
cb = (CircleAndBox)b;
person cdhowie    schedule 02.08.2013
comment
пожалуйста, смотрите обновленный раздел Было бы супер, если: из вопроса. Я так понимаю, что ваше решение этого не касается? - person Dmitry Efimenko; 03.08.2013
comment
@Dmitry C # не имеет множественного наследования, поэтому то, что вы просите, невозможно без использования исключительно интерфейсов и автоматического предоставления методов делегирования через тип прокси во время выполнения. - person cdhowie; 03.08.2013
comment
это звучало умно :) правда, я не совсем понял, что вы сказали. Вы предполагаете, что есть еще способ сделать это, но он слишком сложен? - person Dmitry Efimenko; 03.08.2013
comment
@Dmitry Единственный способ сделать это без реализации каждого метода на CircleAndBox - это создать only interface ICircleAndBox : IBox, ICircle { }, а затем создать реализацию для этого интерфейса во время выполнения. Или написав задачу сборки, которая будет проверять два других интерфейса и генерировать исходный файл C #, реализующий интерфейс. В любом случае, вероятно, больше работы, чем повторение каждой реализации. - person cdhowie; 03.08.2013

Наследование означает «есть».

Ball - это RoundThing. Circle - это RoundThing. Box - это SquareThing. Lamborghini - это Car, а Car - это Transport. Bus и Bicycle тоже Transport.

Если это не отношение «есть», это не наследование. И это объяснение того, почему множественное наследование не поддерживается в C # и Java, и это не приветствуется большинством людей, которых я знаю в C ++. Объект почти никогда не бывает двумя вещами одновременно.

Не злоупотребляйте наследованием просто для экономии строк кода. Даже если сохранение строк кода было законной целью (с чем я не согласен), в этом даже нет необходимости. Здесь вам могут помочь две другие конструкции.

Во-первых, об отношении. То, что вы ищете, - это совершенно другое отношение. Не «является», а «можно использовать как». Вот тут-то и пригодятся интерфейсы. Интерфейсы определяют, в качестве чего можно использовать объект. Таким образом, у вас может быть два интерфейса IBox и ICircle. Тогда ваша CircleAndBox вещь может реализовать оба интерфейса, таким образом, укажите, что она может использоваться как оба.

Во-вторых, вы можете использовать еще одно отношение, чтобы облегчить жизнь. Вы не сохраните строки кода. Но вы убедитесь, что ваш CircleAndBox всегда имеет правильное поведение, определенное в классах Circle и Box. Для этого вы можете использовать агрегацию вместе с шаблоном делегирования. Агрегация - это отношение, которое говорит «имеет». Поэтому сделайте свой CircleAndBox класс частным объектом Circle и частным объектом Box, затем реализуйте интерфейсы ICircle и IBox, просто вызвав соответствующий метод / свойство / событие для соответствующих частных объектов, и верните то, что они возвращают.

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

person Jan Dörrenhaus    schedule 02.08.2013
comment
Меня не очень заботит сохранение строк кода. Меня больше волнует ремонтопригодность. Хорошее сочинение! +1 - person Dmitry Efimenko; 03.08.2013

Используйте интерфейсы:

IBox и ICircle

public interface IBox
{
    Circle Width(string width)
}

public interface ICircle
{
    Circle Radius(string radius)
}

public class Circle : ICircle
{
    private int _radius { get; set; }

    public Circle Radius(string radius)
    {
        _radius = radius;
        return this;
    }
}

public class Box : IBox
{
    private int _width { get; set; }

    public Circle Width(string width)
    {
        _width = width;
        return this;
    }
}

public class CircleAndBox : ICircle, IBox
{
    // should contain methods from both Circle and Box, but return CircleAndBox
}
person Cubicle.Jockey    schedule 02.08.2013
comment
(string Width) все еще плохая идея, несмотря ни на что. Строка не является подходящим типом данных для представления размеров. - person Federico Berasategui; 03.08.2013
comment
Это просто скопируйте и вставьте то, что у них было, и я согласен. - person Cubicle.Jockey; 03.08.2013
comment
Причина, по которой мне не нравится этот подход, заключается в том, что мне все равно придется писать реализацию для каждого метода ICircle и IBox в CircleAndBox. В идеале мне бы хотелось этого избежать. - person Dmitry Efimenko; 03.08.2013

Я не думаю, что вы сможете сделать именно то, что ожидаете, поскольку C # (и .Net в целом) не поддерживает множественное наследование классов. Вы можете унаследовать от одного класса и реализовать интерфейс.

Например:

interface iBox
{
  iBox Width(string width); //Assuming you want Box here and not Circle
}

Тогда у вас есть Box

class Box : iBox ...

Теперь ты можешь иметь

public class CircleAndBox : Circle, iBox
{
private Box _helper;
public iBox Width(string width) { _helper.Width(width); return this; }
}

Это не совсем то, что вы хотите, но я думаю, что это настолько близко, насколько позволяет C #. Если вы хотите использовать слабые типы, существует также метод делегирования для передачи вещей в _helper без явной реализации каждого метода. Однако при прямом делегировании вы вернете не объект CircleAndBox, а объект _helper. Я не уверен, что это послужит цели.

Кроме того, это не сработает, если вы не контролируете хотя бы один из классов, которые хотите наследовать.

person Prof Von Lemongargle    schedule 02.08.2013