2009-03-16 5 views
12

Ich bin neu in Interfaces und abstrakten Klassen. Ich möchte ein paar Schnittstellen erstellen, um Kernmethoden und Variablen für die Objekte eines Einkaufswagensystems zu definieren. Dann möchte ich abstrakte Klassen erstellen, die die Kernfunktionen implementieren. Die Idee ist, dass sie von anderen Klassen auf unterschiedliche Weise für verschiedene Projekte verwendet werden können.Wie definiere ich den Rückgabetyp einer Interface-Methode als eine andere Schnittstelle?

Hier sind meine (abgespeckte) Schnittstellen:

public interface ICart 
{ 
    ... 
    List<ICartItem> CartItems { get; set; } 
} 

public interface ICartItem 
{ 
    int ProductId { get; set; } 
    int Quantity { get; set; } 
} 

Und hier ist meine abstrakte Wagen-Klasse (auch hier nur relevante Linien), die ICART implementiert:

public abstract class Cart : ICart 
{ 

    private List<CartItem> _cartItems = new List<CartItem>(); 

    public List<CartItem> CartItems 
    { 
     get 
     { 
      return _cartItems; 
     } 
    } 
} 

Und mein CartItem Klasse welche implementiert ICartItem:

public abstract class CartItem : ICartItem 
{ 
    ... 
} 

Wenn ich versuche, die Klassen zu kompilieren, erhalte ich einen Fehler s aying: 'Cart' implementiert kein Interface-Element 'CartItems'. 'Cart.CartItems' kann 'ICart.CartItems' nicht implementieren, da es nicht den passenden Rückgabetyp von System.Collections.Generic.List <ICartItem> aufweist.

Ich dachte, dass die Idee hier ist, dass die Schnittstelle von einer Reihe von Klassen implementiert werden kann, die die Kernfunktionen auf verschiedene Arten durchführen und neue Methoden hinzufügen usw. Warum sollte meine Schnittstelle wissen, welche Klasse tatsächlich verwendet wird , solange diese Klasse die Schnittstelle tatsächlich korrekt implementiert?

Antwort

20

Sie müssen Benutzer Generika in C# 2, das erreichen:

public interface ICart<T> where T : ICartItem 
{ 
    // ... 
    List<T> CartItems { get; set; } 
} 

public abstract class Cart : ICart<CartItem> 
{ 
    // ... 
    public List<CartItem> CartItems { get; set; } 
} 
+0

Generika sind als von C# 2.0 :) –

+0

Dies ist ein ziemlich guter Weg, dies zu realisieren! – mquander

+0

(das C# 2-Ding repariert; hoffe, es stört dich nicht ...) –

0

ICART gibt einen Getter und Setter für CartItems aber Ihre Implementierung hat nur bekommen.

4

In Ihrer abstrakten Klasse sollten Sie definieren, dass der Rückgabetyp der CartItems-Eigenschaft vom Typ List<ICartItem> ist.

Aber, wenn Sie wirklich wollen, dass die Eigenschaft des Typs List<CartItem>, können Sie dies vielleicht tun:

public interface ICart<TCartItem> where TCartItem : ICartItem 
{ 
    List<TCartItem> CartItems { get; set; } 
} 

public interface ICartItem 
{ 
} 

public abstract class Cart : ICart<CartItem> 
{ 
    public List<CartItem> { get; set; } 
} 

public abstract class CartItem : ICartItem 
{ 
} 
3

Dies ist eine Frage der contra/Kovarianz.

2 Änderungen sind erforderlich, um diese Kompilierung zu machen.

1) Entfernen Sie die Option "set" in der Eigenschaft der Schnittstelle. (Es ist die Umsetzung nur eine get; Eigenschaft, die am meisten Sinn macht, in jedem Fall)

2) ändern Warenkorb:

 
public abstract class Cart : ICart 
{

private List<CartItem> _cartItems = new List<CartItem>(); 

public List<ICartItem> CartItems 
{ ... 

ich sehr auch das Ändern Ihrer Schnittstelle empfehlen IList zu belichten statt Liste. Die Entwurfsrichtlinien (und FxCop) empfehlen, List in der öffentlichen Schnittstelle nicht verfügbar zu machen. Liste ist ein Implementierungsdetail - IList ist die geeignete Schnittstelle/Rückgabetyp.

2

Eine Schnittstelle ist eine Vereinbarung. Ein Vertrag, wenn du willst, zwischen dir und jedem, der deine Klasse benutzt. Indem Sie Leuten sagen, dass Sie die ICart-Schnittstelle implementieren, versprechen Sie ihnen, dass alle Methoden in der Schnittstelle in Ihrer Klasse existieren. Daher müssen alle Ihre Methoden mit den Signaturen der Schnittstellenmethoden übereinstimmen.

Um die Liste der Elemente, die implementieren ICartItem, zurückgeben, müssen Sie Generika wie von DrJokepu vorgeschlagen verwenden. Dies sagt allen, dass die Schnittstelle nur eine Liste von Objekten bereitstellt, die ICartItem implementieren, nicht speziell ICartItem.

public interface ICart<T> where T : ICartItem 
{ 
    List<T> CartItems { get; set; } 
} 
0

Es gibt 2 Möglichkeiten, dies zu erreichen. Sie können entweder Generika verwenden oder Schnittstellenelemente explizit implementieren.

Generics

public interface ICart<TCartItem> where TCartItem : ICartItem 
{ 
    ... 
    List<TCartItem> CartItems { get; set; } 
} 

public interface ICartItem 
{ 
    int ProductId { get; set; } 
    int Quantity { get; set; } 
} 

public abstract class Cart : ICart<CartItem> 
{ 

    private List<CartItem> _cartItems = new List<CartItem>(); 

    public List<CartItem> CartItems 
    { 
     get 
     { 
      return _cartItems; 
     } 
    } 
} 
public abstract class CartItem : ICartItem 
{ 
    ... 
} 

Explizit implementiert Mitglieder

public interface ICart 
{ 
    ... 
    List<ICartItem> CartItems { get; set; } 
} 
public interface ICartItem 
{ 
    int ProductId { get; set; } 
    int Quantity { get; set; } 
} 

public abstract class Cart : ICart 
{ 
    private List<CartItem> _cartItems = new List<CartItem>(); 

    List<ICartItem> ICart.CartItems 
    { 
     get 
     { 
      return CartItems; 
     } 
    } 
    public List<CartItem> CartItems 
    { 
     get 
     { 
      return _cartItems; 
     } 
    } 
} 
public abstract class CartItem : ICartItem 
{ 
    ... 
} 
Verwandte Themen