Интерфейс против множественного наследования в C #

У меня есть набор класса A и класса B, оба имеют некоторые свойства. и еще один класс C, который имеет свои собственные свойства.

Всякий раз, когда я создаю экземпляр класса C, я хочу получить доступ ко всем свойствам всех трех классов с помощью objClassC.

Как я могу добиться этого на C #?

Я столкнулся с двумя проблемами: -

  1. Я не могу унаследовать оба класса A, B в классе C (C # не поддерживает множественное наследование)
  2. если я использую интерфейс вместо класса A, B (в интерфейсе мы не можем содержать поля)

person Shashank    schedule 04.05.2012    source источник
comment
Вы можете использовать композицию. Также есть миксины   -  person Niklas B.    schedule 04.05.2012


Ответы (6)


Почему бы вам не содержать экземпляр класса A и класса B внутри класса C. Используйте композицию

class C
{
//class C properties
public A objA{get;set;}
public B objeB{get;set;}
}

Тогда вы можете получить доступ

C objc = new C();
objc.objA.Property1 = "something";
objc.objB.Property1 = "something from b";

прочтите статью Состав против наследования

РЕДАКТИРОВАТЬ:

если я использую интерфейс вместо класса A, B (в интерфейсе мы не можем содержать поля)

Ну, интерфейсы не могут содержать полей, если вы определите их, вы получите ошибку компиляции. Но интерфейсы могут содержать свойства, за исключением того, что вы не можете указывать спецификаторы доступа, так как все элементы интерфейса считаются public. Вы можете определить свойства интерфейса «A» и «B» как:

public interface IA
{
     int Property1 { get; set; }
}


public interface IB
{
    int Property2 { get; set; }
}

Затем вы можете реализовать их в классе C, например:

public class C : IA, IB
{
    public int Property1 { get; set; }
    public int Property2 { get; set; }
}

Позже вы можете использовать их как:

C objC = new C();
objC.Property1 = 0;
objC.Property1 = 0;
person Habib    schedule 04.05.2012
comment
Но если реализация IA, IB необходима в классе C, то что нужно для реализации IA и IB? мы можем просто определять свойства без реализации интерфейсов ...? - person Umar Abbas; 04.04.2014
comment
@UmarAbbas, единственным преимуществом этого будет наличие объекта класса C или любого другого класса, реализующего интерфейс IA и IB, в IA или IB и имеющего полиморфное поведение. Приведенный выше код приведен только для примера, я еще не пришел с реальным сценарием, в котором интерфейс просто имеет набор свойств и не имеет методов. - person Habib; 04.04.2014
comment
Но у класса C есть свои свойства. мы также можем создать объект класса C без реализации IA, IB. так почему мы реализовали класс C с интерфейсом IA, IB? - person Umar Abbas; 04.04.2014
comment
а как это множественное наследование, если мы держим объект класса imp в IA, IB? почему мы говорим это множественное наследование? когда нам еще нужны «полиморфизм» и «композиция» Спасибо за ответ! :) - person Umar Abbas; 04.04.2014
comment
@UmarAbbas, Множественного наследования в C # (и насколько я помню Java) нет. Приведенный выше пример объясняет, может ли интерфейс иметь свойства или нет, и в некоторой степени пытается имитировать множественное наследование путем реализации двух интерфейсов. Это НЕ чистое множественное наследование, и в C # нет возможности выполнять множественное наследование. - person Habib; 04.04.2014

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

Interface A
{
   int PropA {get; set;}
}


Interface B
{
  int PropB {get; set;}
}

class C : A, B
{

}

// помещаем этот оператор в какой-нибудь метод

C c = new C();
c.PropA = 1;
c.PropB = 2;
person Adil    schedule 04.05.2012

Интерфейсы не являются решением проблемы множественного наследования. Они просто не делают одно и то же. Самое близкое, что вы можете получить, - это сделать C подклассом A и иметь свойство типа B. Возможно, если вы скажете нам, что должны делать A, B и C, мы сможем дать ответ, который лучше соответствует вашим потребностям ...

person Alejandro B.    schedule 04.05.2012

Интерфейсы могут содержать свойства, например:

public interface IFoo
{
    string Bar { get; set; }
}
person empi    schedule 04.05.2012

Подумайте, как свойства по-разному открываются для клиента при использовании наследования пороков композиции.

Наследование:

    var myCclass = new Cclass;
    myClass.propertyA;
    myClass.propertyB;
    myClass.propertyC;
    // and so on

Сочинение:

 var myCclass = new Cclass;
    myCclass.bClass.propertyB;
    myCclass.aClass.propertyA;
    myCclass.propertyC;

Наследование дает более чистый API - хорошая вещь.

Композиция требует, чтобы я кое-что знал о внутренней структуре класса - не такая уж и хорошая вещь. Это нарушает закон деметры, более известный как принцип наименьшего знания. Вы можете обойти это, имея свойства Cclass, которые один к одному открывают / возвращают свойства Bclass и Aclass - и тогда ваши ссылки Bclass и Aclass будут закрытыми или защищенными в Cclass. И Cclass имеет полный контроль над тем, что раскрыто, а не зависит от A и B, чтобы не публиковать общедоступные материалы, которых вы не раскрывали.

Я согласен с @AlejoBrz, интерфейсы здесь не подходят.

Я также рекомендую «предпочитать композицию наследованию». Но это рекомендация, а не жесткое правило.

person radarbob    schedule 10.08.2012

Интерфейсы не могут содержать полей, но могут содержать свойства. В большинстве случаев свойства можно использовать как поля, и нетрудно сказать:

interface ISomeProperties
  {int prop1 {get;set;}; string prop2 {get; set;}}
interface IMoreProperties
  {string prop3 {get;set;}; double prop4 {get; set;}}
interface ICombinedProperties : ISomeProperties, IMoreProperties; 
  { }

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

Однако следует отметить, что есть несколько вещей, которые можно сделать с полями, но нельзя сделать с помощью свойств. Например, в то время как поле может быть передано Interlocked.Increment, свойство не может; попытка Interlocked.Increment свойства путем копирования его в переменную, вызова Interlocked.Increment для этого, а затем копирования результата обратно в свойство может в некоторых случаях "работать", но потерпит неудачу, если два потока попытаются сделать одно и то же одновременно (это приведет к быть возможно, например, чтобы оба потока читали значение 5, увеличивали его до 6, а затем записывали обратно 6, тогда как наличие двух потоков, вызывающих Interlocked.Increment в поле, которое изначально было равно 5, гарантированно даст 7.).

Чтобы обойти это, может потребоваться, чтобы интерфейс включал некоторые методы, которые либо выполняют блокируемый метод для поля (например, можно иметь функцию, которая вызывает Interlocked.Increment в поле и возвращает результат) и / или включает функции, которые будут вызвать указанного делегата с полем в качестве параметра ref (например,

delegate void ActionByRef<T1>(ref T1 p1);
delegate void ActionByRef<T1,T2>(ref T1 p1, ref T2 p2);
delegate void ActionByRef<T1,T2,T3>(ref T1 p1, ref T2 p2, ref T3 p3);
interface IThing
{ // Must allow client code to work directly with a field of type T.
  void ActOnThing(ActionByRef<T> proc);
  void ActOnThing<ExtraT1>(ActionByRef<T, ExtraT1> proc, ref ExtraT1 ExtraP1);
  void ActOnThing<ExtraT1, ExtraT2>
       (ActionByRef<T> proc, ref ExtraT1 ExtraP1, ref ExtraT2 ExtraP2);
}

Учитывая экземпляр интерфейса, можно сделать что-то вроде:

  theInstance.ActOnThing(
    (ref int param) => Threading.Interlocked.Increment(ref param)
  );

или, если у вас есть локальные переменные maskValue и xorValue и вы хотите атомарно обновить поле с помощью field = (field & maskValue) ^ xorValue:

  theInstance.ActOnThing(
    (ref int Param, ref int MaskValue, ref int XorValue) => {
        int oldValue,newValue;
        do {oldValue = param; newValue = (oldValue & MaskValue) ^ XorValue;
        while (Threading.Interlocked.CompareExchange(ref Param, newValue, oldValue) !=
          oldValue),
    ref maskValue, ref xorValue);
  );

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

person supercat    schedule 06.08.2012
comment
@radarbob: Частично вопрос был связан с тем фактом, что интерфейсы не могут содержать поля. В других ответах признается, что во многих случаях вместо полей можно использовать свойства. Я хотел усилить тот факт, что интерфейсы можно использовать даже тогда, когда кто-то хочет использовать поля способами, для которых свойства не являются приемлемой заменой. - person supercat; 10.08.2012