2009-08-29 10 views
0

Dites que vous écrivez une bibliothèque pour afficher des éléments à l'écran, vous devez donc créer une interface IDisplayable. Cette interface a une méthode pour créer un contrôle à partir de l'objet: displayable.GetControl().Classes génériques avec des méthodes qui ne fonctionnent que pour certains paramètres de type

Vous souhaitez créer votre propre type de liste pouvant être affiché: MyList<T>. Maintenant, cette liste ne peut être affichée que si T est un IDisplayable, donc vous pouvez demander dans la classe MyList que T devrait implémenter IDisplayable. Mais vous voulez aussi utiliser ce type de liste dans certains endroits où T n'est pas IDisplayable (et par conséquent cette liste ne sera pas affichable). Donc est-il possible de dire que MyList implémente IDisplayable si T implémente IDisplayable? Je serais également heureux si MyList<T> implémente toujours IDisplayable mais déclenche une exception lors de l'exécution si vous essayez d'appeler GetControl() si T n'est pas IDisplayable, mais j'aimerais savoir s'il existe un moyen statique de le faire. Cela peut-il être fait? Ou est-ce que je regarde la mauvaise solution?

Edit:

Je suis d'accord avec les suggestions jusqu'à présent que MyList peut avoir trop de responsabilités. Mon idée originale était de créer un MyDisplayableList<T> : MyList<T> (where T : IDisplayable).

Le problème avec cette approche est que j'ai beaucoup de méthodes qui prennent un MyList et retournent un MyList (par exemple des méthodes comme Select dans Linq). Donc, si j'utilise select sur une MyDisplayableList, je récupère une MyList et je ne peux pas l'afficher même s'il s'agit d'une MyList ... existe-t-il un moyen sûr de gérer ce problème en C#?

+1

pourquoi voulez-vous aussi utiliser MyList pour les non-affichable Des classes? Il me semble que vous demandez à MyList de faire trop de choses, mais sans les raisons, c'est difficile à dire. –

Répondre

5

Simple. Vérifiez si le type est IDisplayable. Si ce n'est pas, jetez un InvalidOperationException:

if (!typeof(IDisplayable).IsAssignableFrom(typeof(T))) 
    throw new InvalidOperationException(); 

Ou si vous avez une instance de T, consultez avec que:

IDisplayable disp = instanceOfT as IDisplayable; 
if (disp == null) 
    throw new InvalidOperationException(); 
// do stuff with `disp`. 

Votre conception peut être erronée si. Vous pourriez trop mettre dans une classe et violer le principe de la responsabilité unique. Revérifiez votre conception d'abord.

+0

Merci, c'est ce que je vais utiliser. Mais je préférerais une approche de type sécurité statique si elle est disponible ... – Jules

+0

Il n'y a aucun moyen de l'appliquer de façon statique (pour certaines méthodes spécifiques d'une classe). Les contraintes de type sont appliquées au niveau du type. –

8

Ce n'est pas possible comme vous le décrivez. Vous devez créer deux types de liste:

public class MyList<T> : IList<T> 
{ 
    ... 
} 

public class MyDisplayableList<T> : MyList<T> where T : IDisplayable 
{ 
    ... 
} 
+0

Ah oui merci, c'était ma première idée, mais je ne vous en ai pas assez dit. Le problème avec cette approche est que j'ai beaucoup de méthodes qui prennent un MyList et retournent un MyList (par exemple des méthodes comme Select dans Linq). Donc, si j'utilise select sur une MyDisplayableList, je récupère une MyList et je suis incapable de l'afficher ... existe-t-il un moyen sûr de gérer ce problème en C#? – Jules

+0

Ensuite, faites cette méthode générique: 'public TList Foo (liste TList) où TList: MyList ' – dtb

+0

Merci. Je ne vois pas comment cela résoudrait le problème. Par exemple, si j'ai fait 'aMonList.Select ((a) => a.ToDisplayable())' alors j'aurais une 'MyList ' mais je ne serais pas capable d'appeler GetControl() parce que c'est pas un MyDisplayableList. – Jules

1

Avez-vous besoin de génériques ici?

Je suppose que vous avez plusieurs classes implémentant IDisplayable et que vous souhaitez mettre toutes les instances d'entre eux dans la même liste. Donc, vous auriez juste besoin d'un

public class MyList : Collection<IDisplayable>, IDisplayable 
{ 
    public void GetControl() 
    { 
     foreach (IDisplayable displayable in this) 
     { 
      displayable.GetControl(); 
     } 
    } 
} 

Si vous voulez vraiment mettre des instances non IDisplayable dans cette liste, ainsi, trouver la classe de base commune et définir une

public class MyList2 : Collection<object>, IDisplayable 
{ 
    public void GetControl() 
    { 
     foreach (IDisplayable displayable in this.OfType<IDisplayable>()) 
     { 
      displayable.GetControl(); 
     } 
    } 
} 
1

Je pense que la raison vous voulez MyList<T> pour pouvoir travailler à la fois avec IDisplayable et non avec IDisplayable parce qu'il y a une fonction dupliquée.

Je suggère que vous avez l'implémentation de base comme MyListBase<T> qui implémente les fonctions de base que les deux la liste effectue. MyDisplayableList hérite alors de MyList (MyDisplayableList<T> : MyList<T> where T : IDisplayable), qui exécute des fonctions spécifiques à IDisplayable uniquement.

S'il existe une fonction spécifique à non-IDisplayble, ajoutez NonDisplayableList<T> : MyListBase<T> pour exécuter ces fonctions.

1

Je pense qu'une meilleure solution à ce problème est d'abstraire d'une liste et penser à un composite de IDisplayable s. C'est exactement ainsi que les contrôles sont modélisés dans ASP.NET ou Windows Forms, par exemple.

public class CompositeDisplayable : IDisplayable { 

    public void Add(IDisplayable displayable) { 
    // TODO: check for null, cycles, etc... 
    _list.Add(displayable); 
    } 

    public void Remove(IDisplayable displayable) { 
    // TODO: check for null 
    _list.Remove(displayable); 
    } 

    public Control GetControl() { 
    var control = new Control(); 
    // this assumes Control is also a composite type 
    _list.ForEach(displayable => control.Add(displayable.GetControl())); 
    return control; 
    } 

    private List<IDisplayable> _list = new List<IDisplayable>(); 

} 

Ensuite, pour pont entre votre collection de « quelque chose » au composite, vous pouvez créer une méthode d'extension comme ceci:

public static class DisplayableExtensions { 

    public static IDisplayable ToDisplayable(this IEnumerable source) { 
    if (source == null) throw new NullReferenceException(); // extension method should behave like instance methods 
    var result = new CompositeDisplayable(); 
    source. 
     OfType<IDisplayable>(). 
     ToList(). 
     ForEach(displayable => result.Add(displayable)); 
    return result; 
    } 

} 
Questions connexes