C# FOREACH 在多个列表<>类型上

本文关键字:类型 列表 FOREACH | 更新日期: 2023-09-27 18:31:45

大家好,感谢您的观看。

我认为这是不可能的,但我想通过多个返回的 LIST<> 类型执行相同的 FOREACH,而无需剪切和粘贴代码 4 次。 返回的 dto2、dto3、dto4 和 dto5 列表的所有属性都是相同的,但 DataValue 除外,每个列表的数据类型不同(int、varchar、bool 等)。

var dto2 = rd.EngDetailBitsList(dto.EngId);
var dto3 = rd.EngDetailDateTimesList(dto.EngId);
var dto4 = rd.EngDetailVarCharsList(dto.EngId);
var dto5 = rd.EngDetailVarCharMaxesList(dto.EngId);
foreach (var x in dto2)
{
    var propertyInfo = dto.GetType().GetProperty(x.ShortDescript,
           BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
    if (propertyInfo != null)
    { 
        propertyInfo.SetValue(dto, x.DataValue);
    }
}
foreach (var x in dto3)
{
    var propertyInfo = dto.GetType().GetProperty(x.ShortDescript,
           BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
    if (propertyInfo != null)
    {
        propertyInfo.SetValue(dto, x.DataValue);
    }
}
foreach (var x in dto4)
{
    var propertyInfo = dto.GetType().GetProperty(x.ShortDescript,
           BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
    if (propertyInfo != null)
    {
        propertyInfo.SetValue(dto, x.DataValue);
    }
}
foreach (var x in dto5)
{
    var propertyInfo = dto.GetType().GetProperty(x.ShortDescript,
           BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
    if (propertyInfo != null)
    {
        propertyInfo.SetValue(dto, x.DataValue);
    }
}

C# FOREACH 在多个列表<>类型上

有两种方法可以解决这个问题:

  1. 假设所有dto2dto3dto4dto5都是某种类型T的集合,这些集合实现了与其上声明的ShortDescriptDataValue属性的公共接口。

    var dto2 = rd.EngDetailBitsList(dto.EngId);
    var dto3 = rd.EngDetailDateTimesList(dto.EngId);
    var dto4 = rd.EngDetailVarCharsList(dto.EngId);
    var dto5 = rd.EngDetailVarCharMaxesList(dto.EngId);
    var source = dto2.Cast<MyInterface>
                     .Concat(dto3.Cast<MyInterface>)
                     .Concat(dto4.Cast<MyInterface>)
                     .Concat(dto4.Cast<MyInterface>);
    
    var dtoType = dto.GetType();
    foreach (var x in source)
    {
        var propertyInfo = dtoType.GetProperty(x.ShortDescript,
               BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
        if (propertyInfo != null)
        {
            propertyInfo.SetValue(dto, x.DataValue);
        }
    }
    
  2. 如果没有通用接口,您可以使用dynamic

    var dto2 = rd.EngDetailBitsList(dto.EngId);
    var dto3 = rd.EngDetailDateTimesList(dto.EngId);
    var dto4 = rd.EngDetailVarCharsList(dto.EngId);
    var dto5 = rd.EngDetailVarCharMaxesList(dto.EngId);
    var source = dto2.Cast<dynamic>
                     .Concat(dto3.Cast<dynamic>)
                     .Concat(dto4.Cast<dynamic>)
                     .Concat(dto4.Cast<dynamic>);
    dto.GetType() 
    foreach (var x in source)
    {
        var propertyInfo = dtoType.GetProperty(x.ShortDescript,
               BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
        if (propertyInfo != null)
        {
            propertyInfo.SetValue(dto, x.DataValue);
        }
    }
    

    这将使ShortDescriptDataValue属性在运行时解析,当实际类型上没有此类属性时,你将获得异常。

如果你想要一个全反射解决方案,你可以做一个这样的方法:

static void SetDtoFields<T>(object targetDto, IEnumerable<T> fields)
{
    Type fieldType = typeof(T);
    var fieldNameProp = fieldType.GetProperty("ShortDescript");
    if (fieldNameProp == null || !fieldNameProp.CanRead) 
        return;
    var dataValProp = fieldType.GetProperty("DataValue");
    if (dataValProp == null || !dataValProp.CanRead) 
        return;
    Type targetType = targetDto.GetType();
    foreach (T field in fields)
    {
        var propToSet = targetType.GetProperty((string)fieldNameProp.GetValue(field),
                BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase );
        if (propToSet != null && propToSet.CanWrite &&
            propToSet.PropertyType.IsAssignableFrom(dataValProp.PropertyType))
        {
            propToSet.SetValue(targetDto, dataValProp.GetValue(field));
        }
    }
}

然后在您的主代码中,您可以简单地执行以下操作:

SetDtoFields(dto, rd.EngDetailBitsList(dto.EngId));
SetDtoFields(dto, rd.EngDetailDateTimesList(dto.EngId));
SetDtoFields(dto, rd.EngDetailVarCharsList(dto.EngId));
SetDtoFields(dto, rd.EngDetailVarCharMaxesList(dto.EngId));

这是一个工作演示:https://dotnetfiddle.net/GhrJ0f

我会尝试这样的事情。对于您的 dto2,dto3,dto4,dto5 类,请让它们共享此接口:

public interface IDto
{
    string ShortDescript {get;set;}
    object ObjectValue {get;}
}

在对象上实现 ObjectValue(以一个为例):

public partial class DetailBits // dto2 class maybe?
{
    public object ObjectValue
    {
        get
        {
            return DataValue;
        }
    }
}

然后,创建此函数:

public static void SetValues(DTO dto, IEnumerable<IDto> items)
{
    foreach (var x in items)
    {
        var propertyInfo = dto.GetType().GetProperty(x.ShortDescript,
               BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
        if (propertyInfo != null)
        { 
            propertyInfo.SetValue(dto, x.ObjectValue);
        }
    }
}

最后,您可以在主函数中执行此操作:

var dto2 = rd.EngDetailBitsList(dto.EngId).Cast<IDto>();
var dto3 = rd.EngDetailDateTimesList(dto.EngId).Cast<IDto>();
var dto4 = rd.EngDetailVarCharsList(dto.EngId).Cast<IDto>();
var dto5 = rd.EngDetailVarCharMaxesList(dto.EngId).Cast<IDto>();
SetValues(dto, dto2);
SetValues(dto, dto3);
SetValues(dto, dto4);
SetValues(dto, dto5);

我使用了Martin的#2动态投射解决方案,并进行了一些编辑更改。 效果真棒!

        var dto2 = rd.EngDetailBitsList(dto.EngId);
        var dto3 = rd.EngDetailDateTimesList(dto.EngId);
        var dto4 = rd.EngDetailVarCharsList(dto.EngId);
        var dto5 = rd.EngDetailVarCharMaxesList(dto.EngId);
        var source = dto2.Concat(dto3.Concat(dto4.Concat(dto5.Cast<dynamic>())));
        var dtoType = dto.GetType();
        foreach (var x in source)
        {
            var propertyInfo = dtoType.GetProperty(x.ShortDescript,
                   BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
            if (propertyInfo != null)
            {
                propertyInfo.SetValue(dto, x.DataValue);
            }
        }

你可以试试这个,它所做的只是获取和设置你的 DTO 对象、集合和属性名称。

    var dto2 = rd.EngDetailBitsList(dto.EngId);
    var dto3 = rd.EngDetailDateTimesList(dto.EngId);
    var dto4 = rd.EngDetailVarCharsList(dto.EngId);
    var dto5 = rd.EngDetailVarCharMaxesList(dto.EngId);
    ObjectSetter(new object() /* test only */, dto2, "DataValue");
    private void ObjectSetter(object dto, string dtoProp, 
        IEnumerable items, string itemProperty)
    {
        foreach (var item in items)
        {
            var propertyInfo = item.GetType().GetProperty(dtoProp,
                BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
            var itemValue = dto.GetType().GetProperty(itemProperty,
                BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
            if (propertyInfo != null)
            {
                propertyInfo.SetValue(item, itemValue.GetValue(dto));
            }
        }
    }

您可以通过执行异步处理来提高其性能。

        Task.Factory.StartNew(delegate()
        {
            ObjectSetter(new object() /* test only */, dto2, "DataValue");
        });
        Task.Factory.StartNew(delegate()
        {
            ObjectSetter(new object() /* test only */, dto3, "DataValue");
        });
        Task.Factory.StartNew(delegate()
        {
            ObjectSetter(new object() /* test only */, dto4, "DataValue");
        });
        Task.Factory.StartNew(delegate()
        {
            ObjectSetter(new object() /* test only */, dto5, "DataValue");
        });