2009-03-26 4 views
3

comment est-il possible de savoir si un objet implémente un indexeur ?, je dois partager une logique pour un DataRow et un IDataReader, mais ils ne partagent aucune interface.Question sur les indexeurs et/ou génériques

J'ai essayé aussi avec des génériques mais je ne sais pas quelle restriction devrais-je mettre sur la clause where.

public class Indexer { 
    // myObject should be a DataRow or a IDataReader 
    private object myObject; 
    public object MyObject { 
     get { return myObject; } 
     set { myObject = value; } 
    } 
    // won't compile, myObject has no indexer 
    public object this[int index] { 
     get { return myObject[index]; } 
     set { myObject[index] = value; } 
    } 
    public Indexer(object myObject) { 
     this.myObject = myObject; 
    } 
} 

public class Caller { 
    void Call() { 
     DataRow row = null; 
     IDataReader reader = null; 
     var ind1 = new Indexer(row); 
     var ind2 = new Indexer(reader); 
     var val1 = ind1[0]; 
     var val2 = ind1[0]; 
    } 
} 

Répondre

5

Vous auriez besoin de déclarer une interface avec une propriété indexeur, utiliser cette interface comme la contrainte, et la classe argument de type aurait besoin de mettre en œuvre cette interface afin de satisfaire la contrainte.

Comme vous ne contrôlez pas les classes que vous voulez utiliser, cela ne fonctionnerait pas.

Une alternative est de faire prendre des cours Indexer les get/opérations ensemble en tant que paramètres séparés:

public class Indexer { 

    private Func<int, object> getter;   
    private Action<int, object> setter; 

    public object this[int index] 
    { 
     get { return getter(index); } 
     set { setter(index, value); } 
    } 

    public Indexer(Func<int, object> g, Action<int, object> s) 
    { 
     getter = g; 
     setter = s; 
    } 
} 

public static class IndexerExtensions 
{ 
    public static Indexer ToIndexer(this DataRow row) 
    { 
     return new Indexer(n => row[n], (n, v) => row[n] = v); 
    } 

    public static Indexer ToIndexer(this IDataReader row) 
    { 
     return new Indexer(n => row[n], (n, v) => row[n] = v); 
    } 
} 

Vous pouvez ensuite faire:

DataRow row = null; 
IDataReader reader = null; 
var ind1 = row.ToIndexer(); 
var ind2 = reader.ToIndexer(); 
var val1 = ind1[0]; 
var val2 = ind1[0]; 
+0

Mais je ne peux pas faire en sorte que DataRow implémente mon interface –

+0

Voir la mise à jour de la réponse. –

+0

je ne l'ai pas downvote, FYI, désolé –

1
get { 
    DataRow row = myObject as DataRow; 
    if (row != null) 
     return row[index]; 
    IDataReader reader = myObject as IDataReader; 
    if (reader != null) 
     return reader[index]; 
} 

et utiliser la même logique pour set {}

+0

Je ne veux pas engager dans le coût de Casting ["as" mot-clé] de sorte que la réponse ne me convient pas, imaginez qu'il y ait un lecteur avec 200 enregistrements, ce serait 400 moulages, je pense que ça doit faire mal performance –

+0

Notez que si vous êtes en train de visiter une base de données pour obtenir les données, le coût de 400 conversions est peu susceptible d'être significatif. –

+0

Je suis d'accord avec Earwicker. Je préfère simplement mon approche car elle permet d'avoir plus de sécurité de type - la perf. est probablement parfaitement acceptable en utilisant des cas, mais mon approche vous permet de sauvegarder IDataReader au lieu de l'objet. –

3

Vous pourriez faire de votre indexeur une classe de base abstraite, avec tw o les sous-classes, une pour DataRow et une pour IDataReader.

Pour faciliter l'utilisation, 2 méthodes d'usine pourraient exister, tels que:

 
var ind1 = Indexer.CreateFromDataRow(row); 
var ind2 = Indexer.CreateFromIDataReader(reader); 

Ils pourraient créer une classe de base spécifique pour ce type, avec sa propre logique de manipulation de l'indexation. Cela permet d'éviter la surcharge des contrôles en permanence pour chaque appel get/set (au prix d'une propriété virtuelle unique au lieu d'une propriété standard).