2014-11-25 69 views
3

大家好,感谢您的关注。多个LIST <>类型的C#FOREACH

我不认为这是可能的,但我想通过多个返回的LIST <>类型执行一个完全相同的FOREACH,而不必剪切和粘贴代码4次。的dto2,dto3,dto4的所有属性,并且返回dto5列表是相同的,除了DataValue,这对于每个不同的数据类型(INT,VARCHAR,布尔等)

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); 
    } 
} 
+2

'dto2'''dto5'的类型是什么?他们是否使用'ShortDescription'和'DataValue'属性来实现具有公共基类的通用接口? – MarcinJuraszek 2014-11-25 02:05:47

+0

'ShortDescript'可能会返回不同的值,对吧?所以你可以在'dto'上设置不同的属性。 – MarcinJuraszek 2014-11-25 02:30:24

+0

@BrianRogers我不明白。对我来说,这是有道理的。即使列表中所有项目的类型相同,“ShortDescript”属性也可以返回指向“dto”上不同属性的不同“字符串”值。 – MarcinJuraszek 2014-11-25 02:37:34

回答

2

有解决了两种方式:

  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性能在运行时解决,在没有这样的属性,你会得到一个异常实际类型。

+0

非常简单,Marcin,正是我所期待的!我用#2和动态演员。那里有几个拼写错误,并且Resharper让我消除了一些不必要的转换,所以我在我自己的答案中粘贴了下面的确切代码。测试和工作完全一样,我最初的粘贴代码。 这是我第一次在这里经过数千次寻找解决方案之后问一个问题,所以我无法让任何人赞成,因为我还没有15的声望,或者我会。但是谢谢你! – 2014-11-25 19:18:25

0

如果希望全反射的解决方案,你可以做这样的方法:

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

+0

当声明为'object'时,如何访问'x'上的'DataValue'和'ShortDescript'属性?它甚至不会编译。 – MarcinJuraszek 2014-11-25 02:10:37

+0

好点,你还需要在那里使用GetProperty。我将编辑。 – 2014-11-25 02:11:28

+0

这是我第一次在寻找解决方案的数千次后,在这里问一个问题,所以我不能让任何人赞成,因为我还没有15的声望,或者我会。 – 2014-11-25 19:19:02

-1

你可以试试这个,它只是把你的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"); 
     }); 
+1

反对W/O解释,这听起来不错 – DevEstacion 2014-11-25 06:29:21

+0

这不是我downvote,不知道是谁做的。这是我第一次在寻找解决方案的数千次后,在这里提出一个问题,所以我不能让任何人赞成,因为我还没有15的声望,或者我会。 – 2014-11-25 19:20:06

+0

我也没有倒下。但从我所看到的,你用3个参数调用ObjectSetter,但函数需要4个参数......另外,'IEnumerable items'没有类型可能不会编译。 – Francisco 2014-11-25 20:13:51

0

我会尝试这样的事情。为了您的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); 
+0

这是我第一次在找到解决方案的数千次后,在这里提出一个问题,所以我不能让任何人高兴,因为我还没有15的声望,或者我会。 – 2014-11-25 19:19:44

+0

没问题,如果它可以帮助你的话:)只要特别注意动态演员阵容,它可以非常快速地变得非常脏。 GL – Francisco 2014-11-25 20:09:45

0

我使用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); 
      } 
     } 
相关问题