Подтвердить что ты не робот

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

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

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

Как я могу достичь этого в С#?

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

  • Я не могу наследовать классы A, B в классе C (С# не поддерживает множественное наследование)
  • Если я использую интерфейс вместо класса A, B (в интерфейсе мы не можем содержать поля)
4b9b3361

Ответ 1

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

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";

просмотрите статью Состав и Наследование

EDIT:

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

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

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;

Ответ 2

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

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;

Ответ 3

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

Ответ 4

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

public interface IFoo
{
    string Bar { get; set; }
}

Ответ 5

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

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

    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, интерфейсы здесь не подходят.

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

Ответ 6

public interface IAA
{
    string NameOfA { get; set; }
}
public class AA : IAA
{
    public string NameOfA{get;set;}
}

public interface IBB
{
    string NameOfB { get; set; }
}    
public class BB : IBB
{
    public string NameOfB{get;set;}
}

public class CC : IAA, IBB
{
    private IAA a;
    private IBB b;            

    public CC()
    {
        a = new AA{ NameOfA="a"};
        b = new BB{ NameOfB="b"};
    }

    public string NameOfA{
        get{
            return this.a.NameOfA;
           }
        set{
            this.a.NameOfA = value;
           }
    }

    public string NameOfB
    {
        get{
            return this.b.NameOfB;
        }
        set{
            this.b.NameOfB = value;
        }
    }
}

Ответ 7

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

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);
  );

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