简体   繁体   中英

How do I get a List<T> property in a class via reflection in C#?

I have a class called Animals what contains two List<T> . One is a list of bears, and one is a list of pinguins.

I can pretty trivially get the list of bears by just calling Bears on the animals variable - but how do I get it via reflection?

I have created a static helper class ListHelper , with a generic method that takes either Bear or Pinguin as the generic type, and animals as the argument which should return the list of bears if Bear is the generic type.

That doesn't happen. Instead it crashes with this message : System.Reflection.TargetException: 'Object does not match target type , and I cannot understand why, because the type is correct when I inspect it via the debugger.

Fully "working" example below.

using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System;

class ReflectionTrouble
{
    static void Main(string[] args)
    {
        var animals = new Animals
        {
            Bears = new List<Bear> {new Bear {Name = "Bear1"}, new Bear {Name = "Bear 2"}},
            Pinguins = new List<Pinguin> {new Pinguin {Name = "Pingo1"}, new Pinguin {Name = "Pingo2"}}
        };

        var lists = ListHelper.GetList<Bear>(animals);
        foreach (var bear in lists)
        {
            Console.WriteLine(bear.Name);
        }
        //expected to have printed the names of the bears here...
    }
}

public static class ListHelper
{
    public static IEnumerable<T> GetList<T>(Animals animals)
    {
        var lists = animals.GetType().GetRuntimeProperties().Where(p => p.PropertyType.IsGenericType);
        foreach (var propertyInfo in lists)
        {
            var t = propertyInfo.PropertyType;
            var typeParameters = t.GetGenericArguments();
            foreach (var typeParameter in typeParameters)
            {
                if (typeParameter == typeof(T))
                {
                    // This is where it crashes.
                    var list = (IEnumerable<T>) propertyInfo.GetValue(t);
                    return list;
                }
            }
        }

        return Enumerable.Empty<T>();
    }
}


public class Animals
{
    public IList<Bear> Bears { get; set; }

    public IList<Pinguin> Pinguins { get; set; }
}

public class Bear
{
    public string Name { get; set; }
}

public class Pinguin
{
    public string Name { get; set; }
}

You are misunderstanding how to call propertyInfo.GetValue . The parameter you are passing to it should be the object whose property value you want to get. So in this case you are wanting the value of the property on animals so that line should be:

var list = (IEnumerable<T>) propertyInfo.GetValue(animals);

With this change your code is returning bears to me.

I think a dictionary would be better solution here instead of reflection. Out of the box a dictionary should be able to handle all of your scenarios and offer much better performance. If you want to encapsulate it in a class then it might look like this.

public interface IAnimal
{
    string Name { get; set; }
}

public class Animals
{
    private readonly ConcurrentDictionary<Type, IList<IAnimal>> AnimalDictionary;
    public Animals(IList<IAnimal> animals)
    {
        this.AnimalDictionary = new ConcurrentDictionary<Type, IList<IAnimal>>();
        this.Add(animals);
    }

    public Animals(IAnimal animal)
    {
        this.AnimalDictionary = new ConcurrentDictionary<Type, IList<IAnimal>>();
        this.Add(animal);
    }

    public IList<IAnimal> Get<T>() where T : IAnimal
    {
        if (this.AnimalDictionary.ContainsKey(typeof(T)))
        {
            return this.AnimalDictionary[typeof(T)];
        }
        return (IList <IAnimal>)new List<T>();
    }

    public void Add(IList<IAnimal> animals)
    {
        foreach (IAnimal animal in animals)
        {
            this.Add(animal);
        }
    }

    public void Add(IAnimal animal)
    {
        this.AnimalDictionary.AddOrUpdate(animal.GetType(),
            new List<IAnimal>{animal}, 
            (type, list) =>
                {
                     list.Add(animal);
                     return list;
                });
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM