C # FOREACH for multiple types LIST <>
Hello everyone and thanks for watching.
I don't think this is possible, but I would like to make an identical FOREACH across multiple LIST <> return types without having to cut and paste the code 4 times. All properties of the returned lists dto2, dto3, dto4, and dto5 are the same except for DataValue, which is a different data type for each (int, varchar, bool, etc.).
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); } }
source share
There are two ways to resolve this issue:
-
Assuming that all the
dto2
,dto3
,dto4
anddto5
are collections of some typeT
that implements a common interface with your ad on it propertiesShortDescript
andDataValue
.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); } }
-
Without a regular interface, you can use
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); } }
This will cause the properties
ShortDescript
and toDataValue
be resolved at runtime, and you will get an exception if there is no such type in the current type.
source share
If you want to fully reflect the solution, you can do a method like this:
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)); } } }
Then, in your main code, you can simply:
SetDtoFields(dto, rd.EngDetailBitsList(dto.EngId)); SetDtoFields(dto, rd.EngDetailDateTimesList(dto.EngId)); SetDtoFields(dto, rd.EngDetailVarCharsList(dto.EngId)); SetDtoFields(dto, rd.EngDetailVarCharMaxesList(dto.EngId));
Here is a working demo: https://dotnetfiddle.net/GhrJ0f
source share
I would try something like this. For your dto2, dto3, dto4, dto5 classes, make them share this interface:
public interface IDto { string ShortDescript {get;set;} object ObjectValue {get;} }
Implement ObjectValue in your objects (showing one example):
public partial class DetailBits // dto2 class maybe? { public object ObjectValue { get { return DataValue; } } }
Then create this function:
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); } } }
Finally, you can do this in your main function:
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);
source share
I used Martin # 2 dynamic solution with a few editing changes. Works amazing!
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); } }
source share
You can try this, which it does, just take your DTO object, collection and name of the properties to get and set.
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)); } } }
you can improve performance by doing asynchronous processing. call it like this
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"); });
source share