2012-05-04 4 views
12

Ho un set di Classe A e Classe B entrambi hanno alcune proprietà. e un'altra classe C che ha le proprie proprietà.Interfaccia rispetto all'ereditarietà multipla in C#

Ogni volta che creo un'istanza di classe C, desidero accedere a tutte le proprietà di tutte e tre le classi con objClassC.

Come posso ottenere questo in C#?

I m di fronte a due problemi: -

  1. non posso ereditare sia la classi A, B in classe C (C# non supporta l'ereditarietà multipla)
  2. se io uso l'interfaccia invece di classe A , B (interfaccia non possiamo contiene i campi)
+3

È possibile utilizzare la composizione. C'è anche [mixins] (http://stackoverflow.com/questions/255553/is-it-possible-to-implement-mixins-in-c) –

risposta

29

Perché non contengono istanza di Classe A e Classe B all'interno Classe C. Utilizzare Composition

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

Poi si può accedere

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

check-out l'articolo Composition vs Inheritance

EDIT:

se uso Interfaccia inste annuncio di classe A, B (In Interface non possiamo contiene campi)

Beh, le interfacce non possono contenere campi, se si definisce uno, si otterrà errore di compilazione. Ma le interfacce possono contenere proprietà con l'eccezione che non è possibile specificare lo access specifiers, poiché tutti gli elementi dell'interfaccia sono considerati public. È possibile definire le proprietà per l'interfaccia 'A' e 'B' come:

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


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

Quindi è possibile implementare nella classe C come:

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

In seguito si possono usare come:

C objC = new C(); 
objC.Property1 = 0; 
objC.Property1 = 0; 
+4

+1 per la composizione sull'ereditarietà –

+2

@downvoter, cura di commentare – Habib

+0

Ma se l'implementazione di IA, IB è necessaria in classe C, allora che cosa è necessario implementare IA e IB? possiamo semplicemente definire le proprietà senza implementare interfacce ...? –

2

interfacce possono contenere proprietà, vale a dire .:

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

Le interfacce non sono una soluzione alla mancanza di ereditarietà multipla. Semplicemente non fanno le stesse cose. Il più vicino che puoi ottenere è fare in modo che C sia una sottoclasse di A e avere una proprietà di tipo B. Forse se ci dici cosa devono fare A, B e C, possiamo dare una risposta che si adatta meglio alle tue esigenze ...

4

Le interfacce possono avere proprietà ma se si desidera utilizzare anche i metodi potrebbe essere necessaria la composizione o l'iniezione di dipendenza.

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


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

class C : A, B 
{ 

} 

// mettete queste dichiarazioni in un certo metodo

C c = new C(); 
c.PropA = 1; 
c.PropB = 2; 
1
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; 
     } 
    } 
} 
1

interfacce non possono contenere i campi, ma possono contenere le proprietà. Nella maggior parte dei casi, le proprietà possono essere usati come campi, e non v'è alcuna difficoltà con dire:

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

Data una posizione di tipo ICombinedProperties stoccaggio, si può accedere a tutte e quattro le proprietà direttamente e senza alcun problema.

Va notato, tuttavia, che ci sono alcune cose che possono essere fatte con campi che non possono essere fatti con proprietà. Ad esempio, mentre un campo può essere passato a Interlocked.Increment una proprietà non può; tentativo di Interlocked.Increment una proprietà copiandolo a una variabile, chiamando Interlocked.Increment su quello, e quindi copiare il risultato torna alla proprietà potrebbe "lavoro" in alcuni casi, ma fallirebbe se due thread tentato di fare la stessa cosa contemporaneamente (sarebbe essere possibile ad esempio che entrambi i thread leggano il valore 5, incrementarlo a 6 e quindi riscrivere 6, mentre avere due thread chiama Interlocked.Increment su un campo che inizialmente era pari a 5 sarebbe garantito per ottenere 7.).

Per aggirare questo problema, potrebbe essere necessario che l'interfaccia includa alcuni metodi che eseguono un metodo interbloccato su un campo (ad esempio uno potrebbe avere una funzione che chiama Interlocked.Increment sul campo e restituisce il risultato) e/o includere funzioni che richiederanno un delegato specificato con un campo come parametro ref (es

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

Dato un'istanza dell'interfaccia, si potrebbe fare qualcosa di simile:

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

o, se uno avesse variabili locali maskValue e xorValue e volevano aggiornare atomicamente campo con 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); 
); 

Se ci fossero solo pochi tipi di azioni che si possono desiderare per eseguire sui campi, sarebbe più semplice per includere semplicemente loro all'interno dell'interfaccia. D'altra parte, l'approccio sopra riportato consente a un'interfaccia di esporre i propri campi in modo tale da consentire ai client di eseguire sequenze arbitrarie di azioni su di essi.

+0

-1. non affronta la domanda – radarbob

+0

@radarbob: parte della domanda riguardava il fatto che le interfacce non possono contenere campi. Altre risposte hanno riconosciuto che in molti casi si possono usare proprietà anziché campi. Volevo amplificare il fatto che è possibile utilizzare le interfacce anche quando si desidera utilizzare i campi in modi per i quali le proprietà non sono un sostituto accettabile. – supercat

1

Considerare come le proprietà vengono esposte in modo diverso al client quando si utilizza la composizione vice dell'ereditarietà.

Ereditarietà:

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

Composizione:

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

Inheritance dà un'API più pulito - una buona cosa.

La composizione richiede che conosca qualcosa sulla struttura interna della classe, non una cosa così buona. Questo viola il law of demeter - meglio conosciuto come il principio di minima conoscenza.È possibile aggirare questo avendo proprietà Cclass che espongono/restituiscono le proprietà di classe Bclass & Aclass - e i riferimenti Bclass & Aclass sarebbero quindi privati ​​o protetti in Cclass. AND Cclass ha il controllo totale su ciò che viene esposto anziché dipendere da A & B per non avere materiale pubblico che non è stato esposto.

Sono d'accordo con @AlejoBrz, le interfacce non sono appropriate qui.

Inoltre, faccio un cenno a "Preferisco la composizione sull'ereditarietà". Ma questa è una linea guida, non una regola dura e veloce.