2014-11-25 78 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); 
      } 
     } 
相關問題