6 votes

Comment détecter la longueur d'une propriété d'un tableau en utilisant la réflexion uniquement s'il s'agit effectivement d'un tableau ?

Je convertis un objet basé sur la réflexion comme ceci.

obj.GetType().GetProperties()

Il produit la liste des propriétés attendues.

{System.Reflection.PropertyInfo[4]}
[0] : {System.String Name}
[1] : {System.Guid[] Ids}
[2] : {System.String[] Tags}
[3] : {System.Nullable`1[System.Boolean] Status}

Ensuite, je veux effacer la liste des propriétés non utilisées, et j'ajoute donc la condition selon laquelle la valeur d'une propriété doit être différente de null .

obj.GetType().GetProperties()
  .Where(a => a.GetValue(obj) != null)

Il produit le résultat escompté pour les champs atomiques, mais pas pour les tableaux (car ils ne sont indéfiniment pas des null , seulement vide).

{System.Linq.Enumerable.WhereArrayIterator<System.Reflection.PropertyInfo>}
[0] : {System.String Name}
[1] : {System.Guid[] Ids}
[2] : {System.String[] Tags}

Je ne suis pas sûr de savoir comment détecter les propriétés de tableaux non nuls mais vides afin de les exclure. Je ne peux pas me contenter de convertir un tableau (comme on l'a vu). aquí ) car toutes les propriétés ne sont pas des tableaux.

L'approche la plus proche que j'ai est ce morceau de "travail", qui semble être plutôt... indésirable. Il a cette odeur de code distincte.

obj.GetType().GetProperties()
  .Where(a => a.GetValue(obj) != null 
    && !(a.GetValue(obj) is Array && (a.GetValue(obj) as Array).Length == 0))

Et comme j'essaie de créer quelque chose avec elle en utilisant Select(a=>a.GetValue(obj)) il devient encore plus lourd et plus évident qu'il a besoin d'être amélioré. J'ai également remarqué qu'à chaque fois que le tableau n'est pas vide, mon mapping échoue à produire System.String%5b%5d ce qui m'obligera probablement à clunkifier davantage le code.

2voto

Vous pouvez simplifier la condition en utilisant la recherche de motifs. Cela vous permet d'appeler GetValue une seule fois

p => p.GetValue(obj) is not null and not IList { Count: 0 }

ou avec la version légèrement plus courte

p => p.GetValue(obj) is not (null or IList { Count: 0 })

J'ai testé pour IList au lieu d'un tableau. Cela inclut les tableaux et aussi les ArrayList y List<T> . Notez qu'un tableau expose ses Length par le biais de l'application explicite de la IList.Count également. "explicitement implémenté" signifie que ce membre est caché ou privé à moins d'être vu directement à travers l'interface.

Vous pourriez même tester le ICollection à la place. Cela permettrait d'inclure encore plus de collections.

IList { Count: 0 } est un modèle de propriété qui permet de vérifier si l'objet est un IList disposant d'un Count de 0.

Ensemble :

var result = obj.GetType().GetProperties()
  .Where(p => p.GetValue(obj) is not (null or IList { Count: 0 }));

Notez que cela utilise modèles logiques introduite dans C# 9.0.

1voto

steve16351 Points 4293

Je ne vois pas de solution très différente de la vôtre, mais j'ai l'impression qu'il faut une logique personnalisée pour déterminer si la valeur d'une propriété est "vide" ou non.

Toutefois, le code pourrait être un peu plus propre si l'on utilisait une expression de commutation avec correspondance de motifs, par exemple :

var obj = new MyObject
{
    Name = "Test",
    Ids = new Guid[0],
    Tags = new[] { "t1" },
    Status = null
};

var values = obj.GetType().GetProperties()
    .Select(p => p.GetValue(obj))
    .Where(o => o switch {
        Array array => array.Length > 0,
        _ => o != null
    });

1voto

Tonu Points 253

Malheureusement, l'odeur du code lorsque l'on travaille avec cette réflexion aveugle est en grande partie inévitable (pour autant que je sache) et se condense rarement dans un bloc serré de manière propre.

Votre mappage semble être sensible au type de propriété exact, étant donné que l'élément string[] en faisant allusion à son incapacité à déballer les collections sans aide.

Pour résoudre ces deux problèmes, je suggérerais une aide énumérable verbeuse où les spécificités de la gestion des collections sont immédiatement apparentes pour les futurs mainteneurs.

static IEnumerable<(Type Type, IEnumerable Values)> GetPopulatedPropertyData(object obj)
{
    foreach (var prop in obj.GetType().GetProperties())
    {
        var value = prop.GetValue(obj);
        if (value is Array a)
        {
            if (a.Length == 0) { continue; }
            yield return (prop.PropertyType.GetElementType()!, a);
        }
        // Future collections added in reverse hierarchial order here
        else if (value is not null)
        {
            yield return (prop.PropertyType, new[] { value });
        }
    }
}

Cela devrait faciliter la cartographie en rendant le bon type disponible tout en gardant la majorité du code laid dans sa propre boîte.

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