Интерфейс срещу множествено наследяване в 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