web-dev-qa-db-fra.com

Liste générique d'objets génériques

Disons que j'ai un objet qui représente un champ de données, cet objet a besoin des propriétés suivantes: Nom, Type, Valeur, Longueur. Voici l'objet:

class Field<T>
{
    public string Name { get; set; }
    public Type Type
    {
        get
        {
            return typeof(T);
        }
    }
    public int Length { get; set; }
    public T Value { get; set; }
}  

J'ai utilisé des génériques, car je veux forcer l'utilisateur du code à ne pouvoir attribuer qu'une valeur d'un certain type.
Maintenant, le problème est quand je veux créer une liste de champs.
Si je crée la liste comme List<Field<object>>, nous pouvons affecter n'importe quelle valeur à un champ donné de la liste, et lorsque nous interrogeons pour le type, nous obtenons un 'objet'.
Le problème est que, sur cette liste, il se peut que je veuille quelques champs contenant des chaînes, quelques entrées, des dates et même des objets personnalisés qui auront à leur tour une liste de champs ...
Les génériques sont-ils une bonne solution pour quelque chose comme ça? Si oui, comment pourrais-je m'y prendre pour le mettre en œuvre? Si non, quel est un meilleur moyen? 

---MODIFIER---
Juste pour ajouter un peu plus de fond:
1. Je pourrais vouloir une liste de champs, et chaque champ contiendra un type de données différent, comme ceci: 

List<Field<object>> lst = new List<Field<object>>();
lst.Add(new Field<string>());
lst.Add(new Field<int>());
lst.Add(new Field<SomeObjectFromMyApp>());

2. Plus tard, je devrai interroger ces objets et leurs attributs automatiquement en boucle, quelque chose comme ça: 

foreach(Field<object> fld in lst)
{
    Type t = fld.Type;
    //do some other stuff
}
16
Daniel Gruszczyk

Oui, les génériques est un bon choix. La clé pour atteindre la sécurité de type (et identifier le type avec la propriété Type consiste à ajouter une abstraction entre la liste et la classe Field<T>.

Avez Field<T> implémenter l'interface IField. Cette interface n'a besoin d'aucun membre.

Puis déclarez votre liste comme étant List<IField>.

De cette façon, vous limitez la liste à ne contenir que des champs, mais chaque champ peut être d'un type différent.

Pour lire les valeurs plus tard, il suffit de faire

foreach(var field in list)
{
    var type = field.Type;
    ....
}
14
David Arno

Je vous suggère de définir une interface et Field implémente cette interface

public interface IField
    {

    }

public class Field<T> : IField
    {
        public string Name { get; set; }
        public Type Type
        {
            get
            {
                return typeof(T);
            }
        }
        public int Length { get; set; }
        public T Value { get; set; }
    }

afin que vous puissiez écrire ce code:

var list = new List<IField>();

maintenant cette liste peut contenir n'importe quel objet de type Field<T>

13
VahiD

Comme certains intervenants l'ont déjà mentionné, vous ne pouvez pas accéder à la propriété Type si vous créez une interface vide, je préférerais donc:

public interface IField
{
    Type Type { get; }

    string Name { get; set; }

    int Length { get; set; }
}

public class Field<T> : IField
{
    public string Name { get; set; }

    Type IField.Type => typeof(T);

    public int Length { get; set; }

    public T Value { get; set; }

    public override string ToString()
    {
        return Value.ToString();
    }
}

Ensuite, vous pouvez vérifier quelle propriété est la propriété valeur et transtyper l'objet dans le bon type:

class Program
{
    static void Main(string[] args)
    {
        var fieldList = new List<IField>()
        {
            new Field<string>()
            {
                Value = "Hello World!", 
                Length = 12, 
                Name = "A string"
            },
            new Field<int>()
            {
                Value = 4711,
                Length = sizeof(int),
                Name = "An integer value"
            },
            new Field<double>()
            {
                Value = 2.4,
                Length = sizeof(double),
                Name = "A double value"
            },
        };

        foreach (var field in fieldList)
        {
            if (field.Type == typeof(string))
            {
                PrintField(field, "String value:");
            }
            else if (field.Type == typeof(int))
            {
                PrintField(field, "Integer value:");
            }
            else if (field.Type == typeof(double))
            {
                PrintField(field, "Double value:");
            }
        }
    }

    static void PrintField(IField field, string info)
    {
        Debug.WriteLine(info);
        Debug.WriteLine($"\tName: {field.Name}, Length: {field.Length}, Value: {field}");
    }

Le code produit la sortie suivante:

// String value:
//  Name: A string, Length: 12, Value: Hello World!
// Integer value:
//     Name: An integer value, Length: 4, Value: 4711
// Double value:
//     Name: A double value, Length: 8, Value: 2,4
0
Michael Trapp