web-dev-qa-db-fra.com

Une liste de plusieurs types de données?

J'ai deux classes en tant que telles:

public class MachineLine
{
    public double X1;
    public double Y1;
    public double X2;
    public double Y2;
    public double Thickness;
}

public class MachineCircle
{
    public double CenterX;
    public double CenterY;
    public double Radius;
}

Je veux faire une liste qui peut contenir les deux, mais je ne veux pas que ma liste puisse contenir tout autre type de données. Comment cela peut-il être fait?

46
Adam S

La façon la plus simple de procéder consiste à déclarer une interface et à implémenter les deux types:

public interface IMachine { … }

public class MachineLine : IMachine
{
    public double X1;
    public double Y1;
    public double X2;
    public double Y2;
    public double Thickness;
}

public class MachineCircle : IMachine
{
    public double CenterX;
    public double CenterY;
    public double Radius;
}

Ensuite, vous pouvez le faire:

List<IMachine> m = new List<IMachine>();
59
Dean Chalk

Vous avez quelques réponses expliquant comment faire cela avec une interface vide:

interface IMachineSomething {}
…
var m = new List<IMachineSomething>();

Cela fonctionne très bien si tout ce que vous voulez mettre des objets dans cette liste. Mais que se passe-t-il si vous souhaitez réellement récupérer des objets de la liste? Que pourriez-vous réellement en faire et que vous obligerait la solution ci-dessus à faire?

Ce qui suit montre que vous pouvez faire encore mieux avec une interface non vide.

IMachineSomething est une interface vide. Autrement dit, si vous voulez réellement faire quelque chose avec les objets de votre liste, tout ce que vous allez voir est le contrat vide défini par cette interface. Toutes les fonctionnalités réelles résident dans les types concrets class. Vous devez donc d'abord vérifier leur type, puis effectuer une conversion de type afin d'accéder aux champs publics:

foreach (IMachineSomething sth in m)
{
    if (sth is MachineLine)
    {
        var machineLine = (MachineLine)sth;
        machineLine.DoThis();
    }
    else if (sth is MachineCircle)
    {
        var machineCircle = (MachineCircle)sth;
        machineCircle.DoThat();
    }
    else …
}

Puisque vous travaillez avec un langage orienté objet, il existe une bien meilleure solution: le polymorphisme. Autrement dit, mettez des fonctionnalités communes (propriétés ainsi que méthodes) dans l'interface, de sorte que vous n'aurez pas besoin de faire la distinction entre les types lorsque vous parcourez votre liste:

interface IMachineSomething
{
    void DoSomething();
}

class MachineLine : IMachineSomething
{
    …
    public void DoSomething() { DoThis(); } // example for implicit implementation, or…
}

class MachineCircle : IMachineSomething
{
    …
    void IMachineSomething.DoSomething() { DoThat(); } // …explicit implementation
}

Ce qui vous permet de vous débarrasser des vérifications de type if (sth is …) et des transtypages suivants, et de simplifier votre code à ceci:

foreach (IMachineSomething sth in m)
{
    sth.DoSomething();
}

La boucle n'a plus besoin de se soucier de la façon dont chaque élément est traité exactement. Il suffit de savoir qu'il doit "faire quelque chose" avec un élément, et l'élément lui-même saura ce que cela signifie.

Voir également:

35
stakx

Vous avez 2 façons:

1- Utiliser l'héritage:

public class MachineShape{}

public class MachineLine :MachineShape
{
    public double X1;
    public double Y1;
    public double X2;
    public double Y2;
    public double Thickness;
}

public class MachineCircle : MachineShape
{
    public double CenterX;
    public double CenterY;
    public double Radius;
}

List<MachineShape> m = new List<MachineShape>();


2- Utiliser l'interface:

public interface IMachineShape{}

public class MachineLine : IMachineShape
{
    public double X1;
    public double Y1;
    public double X2;
    public double Y2;
    public double Thickness;
}

public class MachineCircle : IMachineShape
{
    public double CenterX;
    public double CenterY;
    public double Radius;
}

List<IMachineShape> m = new List<IMachineShape>();

Et je recommande l'héritage dans votre cas ...

8
Betamoo

Créez simplement votre propre interface

public interface IMySillyInterface {}

public class MachineLine : IMySillyInterface
{
    public double X1;
    public double Y1;
    public double X2;
    public double Y2;
    public double Thickness;
}

public class MachineCircle : IMySillyInterface
{
    public double CenterX;
    public double CenterY;
    public double Radius;
}

List<IMySillyInterface> list = new List<IMySillyInterface> 
                                {
                                    new MachineCircle(), 
                                    new MachineLine()
                                };
7
Adriaan Stander

Vous pouvez faire en sorte que les deux classes implémentent la même interface et faire de cette interface le type générique contenu de votre List.

6
BoltClock

Maintenant, avec C # 7.0, c'est aussi simple que d'utiliser un tuple:

public List<(MachineLine line, MachineCircle circle)> machineList { get; set; }

Pour ajouter des données (notez le ((et)), une parenthèse pour les paramètres et une autre pour indiquer le tuple):

MachineLine theLine = ...;
MachineCircle theCircle = ...;

machineList.Add((theLine, theCircle));

Pour obtenir des données:

MachineLine ml = emailSender.ElementAt(3).line;
MachineCircle mc = emailSender.ElementAt(3).circle;

Aussi simple qu'un, deux, trois!

2
Adelaiglesia