简体   繁体   English

我如何获得清单 <T> 通过C#中的反射在类中设置属性?

[英]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> . 我有一个叫做Animals的类,其中包含两个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? 我可以通过只在animals变量上调用Bears来轻松获得Bears列表-但是如何通过反射得到它呢?

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. 我创建了一个静态帮助器类ListHelper ,该类具有将BearPinguin作为通用类型的通用方法,并且将动物作为参数,如果Bear是通用类型, 则应返回ListHelper列表。

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. 相反,它崩溃并显示以下消息: System.Reflection.TargetException: 'Object does not match target type ,我不明白为什么,因为通过调试器检查类型时,类型是正确的。

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 . 您误会了如何调用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;
                });
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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