简体   繁体   English

使用C#中的反射添加到未知类型的集合

[英]Add to a collection of unknown type using reflection in c#

So I am using reflection to loop through the properties of one object and populating the values on a different object with properties of the same name. 因此,我正在使用反射来遍历一个对象的属性,并使用相同名称的属性填充另一个对象上的值。 This works great but the problem comes when the property type is a collection. 这很好用,但是当属性类型是一个集合时,问题就来了。 I want to be able to loop through each of the objects in the source collection and populate the same list with objects in the source collection. 我希望能够遍历源集合中的每个对象,并用源集合中的对象填充相同的列表。

public class SourceMessage
{
    public string Name { get; set; }
    public int Version { get; set; }
    public IList<ValueDefinition> Values { get; set; }
}

public class ValueDefinition
{
    public string Name { get; set; }
    public string Value { get; set; }
}

public class TargetObject
{
    public TargetObject()
    {
        Values = new List<TargetValueDefinition>();
    }
    public string Name { get; set; }
    public int Version { get; set; }
    public IList<TargetValueDefinition> Values { get; set; }
}

public class TargetValueDefinition
{
    public string Name { get; set; }
    public string Value { get; set; }
}

Then I use Reflection to populate the target from the source. 然后,我使用反射从源中填充目标。

public static void PopulateFromMessage<T, TS>(ref T targetEntity, TS message)
{
    var sourceType = typeof(TS);
    var targetType = typeof(T);

    foreach (var targetPropInfo in targetType.GetProperties())
    {
        if (sourceType.GetProperty(targetPropInfo.Name) != null)
        {
            var obj = sourceType.GetProperty(targetPropInfo.Name);
            if (obj.PropertyType.Namespace == "System.Collections.Generic")
            {
                //var x = targetType.GetProperty(targetPropInfo.Name);
                //PopulateFromMessage(ref x, sourceType.GetProperty(targetPropInfo.Name));
                continue;
            }
            targetPropInfo.SetValue(targetEntity, sourceType.GetProperty(targetPropInfo.Name).GetValue(message), null);
        }
    }
}

So calling this would be like this: 因此,调用此方法将是这样的:

private void DenormalizeMessage(SourceMessage message)
{
    var newTargetObject = new TargetObject();
    PopulateFromMessage(ref newTargetObject , message);
}

I can identify when the property is a collection but am uncertain of how to create new TargetValueDefinitions and populate them with the values from ValueDefinitions. 我可以确定该属性何时是一个集合,但不确定如何创建新的TargetValueDefinitions以及如何使用ValueDefinitions中的值填充它们。 In the end it is pretty much a copy of the SourceMessage in the form of a TargetObject. 最后,它几乎是TargetObject形式的SourceMessage的副本。

This all stems from receiving messages and transforming them into objects with the same property names. 这一切都源于接收消息并将它们转换为具有相同属性名称的对象。

You should create a interface for each class (implement the methods and properties on interface) and implement it in each class. 您应该为每个类创建一个接口(在接口上实现方法和属性),并在每个类中实现它。 After, in function PopulateFromMessage should specify the interface allowed in method, with this you can use directly the properties of class with T and TS generic types. 之后,在函数PopulateFromMessage中应指定方法所允许的接口,这样您就可以直接使用具有T和TS泛型类型的类的属性。

Disclaimer: This is extremely unsafe to do and makes a lot of assumptions but it should puth you on the right path. 免责声明:这样做是非常不安全的,并且会做出很多假设,但它应该使您走上正确的道路。

Change you method to this: 将您的方法更改为此:

public static void PopulateFromMessage<T, TS>(T targetEntity, TS message)
    {
        var sourceType = typeof (TS);
        var targetType = typeof (T);

        foreach (var targetPropInfo in targetType.GetProperties())
        {
            if (targetPropInfo.PropertyType.IsGenericType)
            {
                if (targetPropInfo.PropertyType.GetGenericTypeDefinition() == typeof(IList<>))
                {
                    var originalList = sourceType.GetProperty(targetPropInfo.Name).GetValue(message) as IList;

                    if (originalList != null)
                    {
                        var argumentType = targetPropInfo.PropertyType.GetGenericArguments();
                        var listType = typeof (List<>);
                        var concreteType = listType.MakeGenericType(argumentType);
                        var newList = Activator.CreateInstance(concreteType) as IList;

                        foreach (var original in originalList)
                        {
                            var targetValue = Activator.CreateInstance(argumentType[0]);

                            // do this yourself. Here we're converting ValueDefinition to TargetValueDefinition
                            // targetValue.Fill(original);
                        }

                        targetPropInfo.SetValue(targetEntity, newList);
                    }
                }
            }
            else
            {
                if (sourceType.GetProperty(targetPropInfo.Name) != null)
                {
                    var obj = sourceType.GetProperty(targetPropInfo.Name);
                    if (obj.PropertyType.Namespace == "System.Collections.Generic")
                    {
                        //var x = targetType.GetProperty(targetPropInfo.Name);
                        //PopulateFromMessage(ref x, sourceType.GetProperty(targetPropInfo.Name));
                        continue;
                    }
                    targetPropInfo.SetValue(targetEntity, sourceType.GetProperty(targetPropInfo.Name).GetValue(message), null);
                }
            }
        }
    }

If your problem is iterating through items contained inside a single property when it is a collection, then the key would be to read the property value into a dynamic variable and not an object variable that is by default, this way you could use a foreach for it. 如果您的问题是遍历单个属性(当它是一个集合时)中包含的项目,那么关键是将属性值读入动态变量而不是默认情况下的对象变量,这样您就可以将foreach用于它。

dynamic propVal = inputProperty.GetValue(item);
foreach (var subItem in propVal)
{    
//do your stuff
}

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

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