4 votes

Utilisation de la présence d'un indexeur comme paramètre de la méthode signature/contrat/type

A titre d'exemple, je vais utiliser les classes SqlDataReader et DataRow : elles définissent toutes deux l'indexeur suivant :

public object this[int columnIndex] { get; set; }

Quel est le type de plus petit dénominateur commun à utiliser comme type de paramètre de la méthode, de sorte que les deux (et toute autre classe implémentant le même indexeur) puissent être passés et utilisés de la même manière, par ex :

void DoSomething(??? indexedObject)
{
   string foo = indexedObject[0].ToString(); 
          // let's ignore the presence of ToString()
          // for the purpose of this question
}

Est-ce que c'est object ? Que se passe-t-il si l'objet indexé ne dérive pas de object (Je pense que c'est possible, même si c'est très peu probable).

Si cela a de l'importance, je cible .NET 3.5.

Modifier : Je cherche une application du contrat qui oblige les appelants à passer des objets qui implémentent ledit indexeur.

7voto

Reed Copsey Points 315315

C'est un objet ?

A peu près. Il n'y a pas d'interface ou de classe commune que vous pouvez utiliser, donc object est vraiment la seule chose garantie partagée dans la hiérarchie.

Que faire si l'objet indexé ne dérive pas d'un objet ?

Cela n'est pas possible en .NET. System.Object est le type de base de tous les types, et les valeurs peuvent toujours être traitées comme un objet (même si elles nécessitent une mise en boîte pour que cela fonctionne).

Cependant, en passant en tant que object ne donnera pas accès à l'indexeur, autrement que par réflexion.

Le seul moyen direct d'y parvenir serait d'utiliser la méthode suivante dynamic mais cela nécessite .NET 4 (et n'est pas sûr au niveau des types, ce qui signifie que vous pourriez obtenir des exceptions d'exécution).

Une meilleure approche pourrait consister à fournir un Func<T, int, string> ce qui vous permettrait, à l'emplacement de l'appel, de préciser comment extraire la valeur :

void DoSomething<T>(T object, Func<T, int, string> valueExtractor)
{
    string foo = valueExtractor(object, 0); 
}

Alors appelez via :

DoSomething(indexedObject, (o,i) => o[i].ToString());

Cela vous permet de passer un objet et un mécanisme pour extraire une valeur donnée par un index au site d'appel, qui fonctionne pour n'importe quel type.


Editer en ce qui concerne :

Edit : Je cherche une application du contrat qui oblige les appelants à passer des objets qui implémentent ledit indexeur.

Il n'existe pas de contrat ou d'interface intégré que ces types mettent en œuvre, ni de moyen de contraindre un générique sur la base de l'existence d'un indexeur. Vous aurez besoin d'une approche différente, comme ma suggestion d'utiliser un délégué pour extraire la valeur.

1voto

asawyer Points 10642

Je dirais que Reed Copsey's Func<T> la solution du délégué est la meilleure solution mais comme vous êtes en C# 3.5, vous devrez définir vos propres délégués, Func<T> ne sera pas disponible. C'est pourtant très simple.

Edit - Oops, c'était 3.5 et non 4.0.

Pour ce que cela vaut, voici une autre solution qui, comme je l'ai souligné dans un commentaire, utilise une interface pour définir des types d'adaptateurs qui comprennent comment appeler les indexeurs de type spécialisé mais permettent au site d'appel ( DoSomething ) pour travailler avec l'interface commune :

void Main()
{
    var t1 = new Type1(); // ie Sql Reader
    var t2 = new Type2(); // ie DataRow

    DoSomething(new Type1IndexAdapter(t1));
    DoSomething(new Type2IndexAdapter(t2));
}

public void DoSomething(ICanIndex indexer)
{
    var r = indexer["test"];
}

public interface ICanIndex
{
    string this[string index]{get;}
}

public class Type1IndexAdapter : ICanIndex
{
    public Type1 value;
    public Type1IndexAdapter(Type1 val)
    {
        this.value = val;
    }
    public string this[string index]
    {
        get
        {
            return this.value[index];
        }
    }
}

public class Type2IndexAdapter : ICanIndex
{
    public Type2 value;
    public Type2IndexAdapter(Type2 val)
    {
        this.value = val;
    }
    public string this[string index]
    {
        get
        {
            return this.value[index];
        }
    }
}

public class Type1 // ie SqlDataReader 
{
    public string this[string index]
    {
        get
        {
            Console.WriteLine("Type 1 indexer called: " + index);
            return null;
        }
    }
}

public class Type2 // ie DataRow 
{
    public string this[string index]
    {
        get
        {
            Console.WriteLine("Type 2 indexer called: " + index);
            return null;
        }
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X