0
votes

Comment déterminer si toutes les propriétés de type List <T> dans un objet sont nulles ou vides?

J'ai un objet qui contient quelques propriétés int / string et des propriétés List <T> où T sont d'autres classes dans le projet lui-même. Existe-t-il un moyen plus propre de déterminer si seules ces propriétés List <T> sont vides ou nulles? Peut-être en utilisant une instruction Linq?

J'ai essayé de faire une recherche à ce sujet mais je ne trouve pas de moyen court et propre. Dois-je opter pour la réflexion ?? Quelqu'un peut-il fournir un échantillon à ce sujet?

var matchFound = myObject.GetType().GetProperties()
                        .Where(x =>(x.GetValue(myObject) as IList)?.Count()>0);

EDIT 1: Jusqu'à présent, j'ai réussi à écrire un code propre pour vérifier si les propriétés de la liste sont nulles. Mais comment puis-je vérifier s'ils sont vides. J'ai besoin de convertir un objet en liste mais je ne connais pas le type de liste dont il s'agit

var matchFound = myObject.GetType().GetProperties()
                        .Where(x => x.PropertyType == typeof(List<>))
                        .Select(x => x.GetValue(myObject))
                        .Any(x => x != null);

EDIT 2: J'ai fini par utiliser ceci, une seule ligne qui fonctionne bien:

public class A
{
   ..some properties..
   List<ClassA> ListA { get; set; }
   List<ClassB> ListB { get; set; }
   List<ClassC> ListC { get; set; }
   List<ClassD> ListD { get; set; }
   ..some properties..
}

c#

8 commentaires

Cela devrait être possible avec la méthode List <T> .TrueForAll .


mon exigence est que j'ai un objet de la classe indiquée ci-dessus. il y a beaucoup de listes donc je ne veux pas les vérifier individuellement si ListA est vide ou pas alors ListB est vide ou pas et ainsi de suite .... est-ce réalisable par linq ou réflexion ??


essayez de considérer la traversée récursive de toutes les propriétés en utilisant la réflexion - stackoverflow.com/questions/20554103/...


@ChrisRollins: Je ne vois pas comment TrueForAll serait utile ici - les listes elles-mêmes ne sont vérifiées que pour être vides (ou une référence nulle). Cela ne nécessite d'examiner aucun élément de la liste.


Si vous ne connaissez pas le type générique, mais que vous connaissez le nom de la propriété, il peut être plus facile et plus rapide d'utiliser simplement myObject.ListA?.Cast<object>().Any() == true . Répétez pour toutes les propriétés de type 'List <T>'. Vous pouvez utiliser la réflexion pour ce faire (ou les arbres d'expression), mais l'utilisation de la réflexion est beaucoup plus lente que la simple conversion en objet


malheureusement, je ne connais pas non plus les noms de propriétés ... en essayant d'écrire quelque chose de totalement générique ... mais votre option semble prometteuse


Notez que .Where(x => x.PropertyType == typeof(List<>)) ne trouvera jamais aucune propriété. Une propriété ne serait pas de type List<> , car c'est le type générique ouvert. Vous devez détecter que la propriété est d'un type qui est un type construit à partir de List<> .


Donc, vous voulez faire cela pour toute classe qui pourrait avoir un nombre quelconque de propriétés de List<TypeInYourAssembly> ?


4 Réponses :


0
votes

Vous pouvez utiliser la réflexion pour vos besoins, je viens de l'essayer.

UserDetails<Test> yourObject = new UserDetails<Test>();
            yourObject.Test1 = new List<Test> { new Test() };

            var result = typeof(UserDetails<Test>).GetProperties()
                .Select(prop => prop)
                .Where(property =>
                {
                    if (property.PropertyType == typeof(List<Test>))
                    {
                        var value = (List<Test>)property.GetValue(yourObject, null);
                        return value == null || value.Count == 0;
                    }

                    return false;
                }).ToList();

Utilisez cette requête pour rechercher, vous pouvez personnaliser la condition où pour vos besoins

class UserDetails<T>
    {
        public List<T> Test1 { get; set; }
        public List<T> Test2 { get; set; }
        public string firstname { get; set; }
        public string surname { get; set; }
        public string city { get; set; }
        public string state { get; set; }
    }

Mettre à jour si vous utilisez Templete

UserDetails yourObject = new UserDetails();
            yourObject.Test1 = new List<Test> { new Test() };

            var result = typeof(UserDetails).GetProperties()
                .Select(prop => prop)
                .Where(property =>
                {
                    if (property.PropertyType == typeof(List<Test>))
                    {
                        var value = (List<Test>)property.GetValue(yourObject, null);
                        return value == null || value.Count == 0;
                    }

                    return false;
                }).ToList(); // this will return 1 because 1 property has count > 1

Requete

       class Test
       {

       }
        class UserDetails
        {
            public List<Test> Test1 { get; set; }
            public List<Test> Test2 { get; set; }
            public string firstname { get; set; }
            public string surname { get; set; }
            public string city { get; set; }
            public string state { get; set; }
        }


5 commentaires

je l'ai déjà fait mais j'ai un problème. si je fais référence à votre code la classe Test ne m'est pas connue ... ça peut être n'importe quelle classe d'où la question


vous pouvez changer votre classe ClassA, ClassB c'est pareil


comme je l'ai déjà dit .... ces classes ne me sont pas connues dans l'objet ... il n'y a pas de liste définitive ..


Pouvez-vous poster votre classe pour interroger


je ne peux pas ... termes et conditions



4
votes

Voici ce que je ferais.

var matchFound = myObject.GetType().GetProperties()
                        .Where(x =>(x.GetValue(myObject) as IList)?.Count()>0);

Puis

var matchFound = myObject.GetType().GetProperties()
                        .Where(x => x.PropertyType.GetActualType() != x.PropertyType && 
                              (x.GetValue(myObject) as IList)?.Count()>0);

Vous pouvez en fait faire encore mieux et n'avez pas besoin de vérifier le type et d'essayer uniquement de convertir la valeur. La valeur sera toujours nulle si le type n'est pas un IList

    /// <summary>
    /// caching a Dyctionary of IList types for faster browsing
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    private static readonly Dictionary<Type, Type> CachedActualType = new Dictionary<Type, Type>();
    // Get Internal type of IList.
    // When the type is not a list then it will return the same type.
    // if type is List<T> it will return the type of T
    public static Type GetActualType(this Type type)
    {
        if (CachedActualType.ContainsKey(type))
            return CachedActualType[type];

        if (type.GetTypeInfo().IsArray)
            CachedActualType.Add(type, type.GetElementType());
        else if (type.GenericTypeArguments.Any())
            CachedActualType.Add(type, type.GenericTypeArguments.First());// this is almost always find the right type of an IList but if it fail then do the below. dont really remember why this fail sometimes.
        else if (type.FullName?.Contains("List`1") ?? false)
            CachedActualType.Add(type, type.GetRuntimeProperty("Item").PropertyType);
        else
            CachedActualType.Add(type, type);

        return CachedActualType[type];
    }


2 commentaires

OMG comment pourrais-je rater ça ... ouais, je pourrais simplement jeter la clause où ... ça marche, c'est rapide et c'est propre à maintenir ... merci .. !!


Heureux d'avoir pu aider :)



0
votes

Vous avez besoin d'un peu de réflexion:

// the type to test
public class TestData
{
    public string A { get; set; }
    public List<string> B { get; set; }
    public List<int> C { get; set; }
}

// an helper class used to generate checking functions
public static class ListTester
{
    public static Func<T, bool> MakeClassChecker<T>()
        where T : class
    {
        var checkFunctions = EnumerateListProperties<T>()
            .Select(MakePropertyChecker<T>)
            .ToList();

        return instance => checkFunctions.All(f => f(instance));
    }

    public static IEnumerable<PropertyInfo> EnumerateListProperties<T>()
    {
        return typeof(T).GetProperties(Instance | Public | NonPublic)
            .Where(prop => IsListClosedType(prop.PropertyType));
    }

    public static Func<T, bool> MakePropertyChecker<T>(PropertyInfo prop)
        where T : class
    {
        var propType = prop.PropertyType;
        var listItemType = propType.GenericTypeArguments[0];

        var listEmptyChecker = (Func<object, bool>) ListCheckerFactoryMethod
            .MakeGenericMethod(listItemType).Invoke(null, new object[0]);

        return instance => instance != null && listEmptyChecker(prop.GetValue(instance));
    }

    private static MethodInfo ListCheckerFactoryMethod
        = typeof(ListTester).GetMethod(nameof(ListCheckerFactory), Static | Public);


    public static Func<object, bool> ListCheckerFactory<T>()
    {
        return list => list == null || ((List<T>) list).Count == 0;
    }

    public static bool IsListClosedType(Type type)
    {
        return type != null &&
                type.IsConstructedGenericType &&
                type.GetGenericTypeDefinition() == typeof(List<>);
    }
}

[Test]
public void TestTemp()
{
    var props = ListTester.EnumerateListProperties<TestData>();
    CollectionAssert.AreEquivalent(props.Select(prop => prop.Name), new[] {"B", "C"});

    var allListsAreNullOrEmpty = ListTester.MakeClassChecker<TestData>();

    Assert.That(allListsAreNullOrEmpty(new TestData()), Is.True);
    Assert.That(allListsAreNullOrEmpty(new TestData() {B = new List<string>()}), Is.True);
    Assert.That(allListsAreNullOrEmpty(new TestData() {B = new List<string>() {"A"}}), Is.False);
}

Maintenant, pour les bits importants: vous recherchez les propriétés des types génériques fermés de List<> . La sélection des propriétés se fait dans IsListClosedType . Ensuite, pour chaque propriété, nous MakePropertyChecker une fonction de vérification à l'aide de MakePropertyChecker .

Le travail de MakePropertyChecker est de construire via MakeGenericMethod une version de ListCheckerFactory du type approprié.


0 commentaires

0
votes

Vous voulez vérifier toutes les propriétés qui sont de type List<something> Cette méthode peut faire l'affaire:

var matchFound = myObject.GetType().GetProperties()
    .Where(p => IsGenericList(p.PropertyType))
    .Select(p => p.GetValue(myObject) as IEnumerable)
    .Any(list => list != null && list.Cast<object>().Any());//Cast<object> needed to be able to use Linq Any()

Vous pouvez maintenant modifier votre requête Linq pour qu'elle renvoie si au moins un des membres de la liste n'est pas nul ou vide

bool IsGenericList(Type t)
{
    return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>);
}


0 commentaires