Как отфильтровать коллекцию внутри универсального метода
У меня есть два класса, который имеет следующие свойства
Class A
{
public int CustID { get; set; }
public bool isProcessed { get; set; }
}
Class B
{
public int EmpId{ get; set; }
public bool isProcessed { get; set; }
}
Я создал один универсальный метод, который принимает все эти классы. Свойство isProcessed является общим для обоих этих классов.
public void ProceesData<T>(IList<T> param1, string date1)
{
}
Мне нужны следующие вещи
- Внутри метода ProcessData я хочу отфильтровать элементы, для которых флаг isProcessed имеет значение "True".
- Также я хочу повторить эту коллекцию и нужно установить значения для свойства IsProcessed.
Примечание: я предпочитаю решение с использованием отражения, так как имя свойства является константой (то есть "IsProcessed")
Может ли кто-нибудь помочь в этом.
3 ответа
Самый простой способ - убедиться, что оба класса реализуют общий интерфейс и ограничивают ваш общий метод. Например:
public interface IProcessable
{
bool isProcessed { get; set; }
}
public class A : IProcessable
{
public int CustID { get; set; }
public bool isProcessed { get; set; }
}
public class B : IProcessable
{
public int EmpId { get; set; }
public bool isProcessed { get; set; }
}
Теперь ваш метод будет выглядеть так:
public void ProceesData<T>(IList<T> param1, string date1)
where T : IProcessable // <-- generic constraint added
{
foreach (var element in param1)
{
element.isProcessed = true;
}
}
Другой вариант, который более полезен, если вы не можете использовать интерфейс или имена свойств могут отличаться, заключается в передаче Action<T>
в качестве параметра для вашего метода. Например:
public void ProceesData<T>(IList<T> param1, string date1, Action<T> func)
{
foreach (var element in param1)
{
func(element);
}
}
И назовите это так:
ProceesData<A>(list, "", x => x.isProcessed = true);
Создать интерфейс, как IProcessData
, который содержит логическое свойство, IsProcessed
, Пусть оба класса реализуют этот интерфейс. Измени свой ProcessData
метод, так что он больше не имеет общего обозначения (<T>
) и принимает IList<IProcessData>
, Затем выполните фильтрацию и итерации данных param1.
Примечание: я предпочитаю решение с использованием отражения
Этот метод будет повторять коллекцию, фильтровать в соответствии с propertyName
а также filterValue
и установите значения в newValue
используя отражение:
public void ProceesData<T>(IList<T> param1, string date1, string propertyName,
object filterValue, object newValue)
{
PropertyInfo pi = typeof(T).GetProperty(propertyName);
object value;
for (int i = param1.Count; i <= 0; i--)
{
value = pi.GetValue(param1[i]);
if (value.Equals(filterValue))
{
pi.SetValue(param1[i], newValue);
}
}
}
Вы можете назвать это так:
ProceesData<A>(a_list, "", "isProcessed", false, true);
Отказ от ответственности:
Хотя это возможно. Это далеко не сохранить. Если вы передадите неправильное имя свойства, это не удастся! Я бы порекомендовал использовать второй подход, передав @DavidG Action
делегировать. Это сделает всю обработку более надежной и менее подверженной ошибкам. Я бы предложил использовать здесь обычный обратный цикл for, потому что это даже позволит вам удалить элементы из вашей коллекции.
public static void ProceesData<T>(IList<T> param1, string date1, Action<T> func)
{
for (int i = param1.Count; i <= 0; i--)
{
func(param1[i]);
}
}
Этот вызов даст вам тот же результат:
ProceesData<A>(a_list, "", (x)=> { if (!x.isProcessed) x.isProcessed = true; });
Благодаря этому подходу вы становитесь еще более гибкими, поскольку при каждом вызове вы можете решить, что должен делать этот метод. Вы даже можете удалить обработанные предметы из коллекции:
ProceesData<A>(a_list, "", (x)=> { if (!x.isProcessed) a_list.Remove(x); });
Одно отличие остается, хотя. Потому что, если у вас есть коллекция, которая будет содержать оба элемента A
а также B
как это: (я использовал примерный конструктор для этого случая)
List<object> obj_list = new List<object>()
{
new A(1, false),
new B(2, true),
new A(3, false),
new B(4, false),
};
Вы должны будете использовать dynamic
тип данных для Action
перегрузка:
ProceesData<dynamic>(obj_list, "", (x) => { if (!x.isProcessed) obj_list.Remove(x); });
И для того, чтобы сделать способ отражения, вам нужно будет проверять тип на каждой итерации. Это увеличит время обработки.
public static void ProceesData<T>(IList<T> param1, string date1, string propertyName, object filterValue, object newValue)
{
for (int i = param1.Count-1; i >= 0; i--)
{
PropertyInfo pi = param1[i].GetType().GetProperty(propertyName);
object value;
value = pi.GetValue(param1[i]);
if (value.Equals(filterValue))
{
pi.SetValue(param1[i], newValue);
}
}
}