У меня есть требование создать объект "фабрика", который работает следующим образом:
(Для простоты мы можем предположить, что все свойства являются строками)
Например, из этого списка объектов-лиц:
Person1 { Name: "Bob", Job: "Policeman", Location: "London" }
Person2 { Name: "John", Job: "Dentist", Location: "Florida" }
Person3 { Name: "Mike", Job: "Dentist", Location: "London" }
Person4 { Name: "Fred", Job: "Doctor", Location: "London" }
Если бы я передал список, содержащий людей 2 и 3, он вернул бы нового человека следующим образом:
Name: "No Match", Job: "Dentist", Location "No Match"
Если бы я прошел через человека 3 и 4, он вернул бы нового человека:
Name: "No Match", Job: "No Match", Location "London"
Уже....
Используя ответ на этот вопрос SO
:
Как проверить, имеют ли все элементы списка одинаковое значение, и вернуть его или вернуть «другое значение», если это не так?
Я могу заставить этот LINQ работать с одним известным объектом, но мне нужно, чтобы он был универсальным. Это охватывает только одно конкретное свойство, но мои объекты имеют более 30 свойств.
var otherValue = "No Match"
var matchingVal= people.First().Job;
return people.All(x=>x.Job== matchingVal) ? matchingVal: otherValue;
Я также знаю, что могу использовать отражение, чтобы получить список свойств в моем объекте. Но как все это совместить в одну «фабрику» — непостижимо.
Я не думаю, что это уникальная проблема, но я не могу найти полное решение ни в одном из моих поисков. Может быть, уже есть пакет Nuget, который может мне помочь?
Все советы с благодарностью получил.
@Oliver Идентификатор был просто для ссылки в моем примере. Я изменил его, чтобы было понятнее. Все свойства должны быть сопоставлены.
Где для каждого типа определено, что должно быть вставлено, если совпадение не найдено (например, int = -1, string = null или string = «нет совпадения», DateTime = DateTime.MaxValue и т. д.)
Мы можем рассматривать все как строку. Поэтому, если строки совпадают, новый объект будет иметь это значение. Если они этого не делают, новый объект получает произвольное значение в этом свойстве, например: «Нет совпадения».
Ваш ввод представляет собой перечисление объектов определенного типа, и вы должны создать объект того же типа, где все свойства заполнены, где все значения одинаковы. Это можно сделать примерно так:
private static T GetCommonProperties<T>(IEnumerable<T> source) where T : new()
{
var first = true;
var common = new T();
var props = typeof(T).GetProperties();
foreach (var item in source)
{
if (first)
{
first = false;
foreach (var prop in props)
{
var value = prop.GetValue(item, null);
prop.SetValue(common, value);
}
}
else
{
foreach (var prop in props)
{
var itemValue = prop.GetValue(item, null);
var commonValue = prop.GetValue(common, null);
if ((dynamic)itemValue != (dynamic)commonValue)
{
prop.SetValue(common, GetDefault(prop.PropertyType));
}
}
}
}
return common;
}
Данный метод на самом деле не является оптимальным, поскольку он использует трюк dynamic
для решения проблемы сравнения значений в коробках. Кроме того, получение значения по умолчанию для определенного типа, вероятно, может быть реализовано с помощью этого общего подхода:
private static object GetDefault(Type t)
{
return typeof(Program)
.GetMethod(nameof(GetDefaultValue), BindingFlags.NonPublic | BindingFlags.Static)
.MakeGenericMethod(t)
.Invoke(null, null);
}
private static T GetDefaultValue<T>()
{
return default;
}
Но также можно было бы предоставить оператор switch или Dictionary<Type, object>
, который возвращает желаемое значение по умолчанию, если совпадение отсутствует.
И последнее, но не менее важное: еще одним возможным улучшением производительности будет удаление всех записей PropertyInfo
из переменной props
, потому что для любого следующего предстоящего объекта эта проверка больше не нужна, и точно так же цикл может быть досрочно завершен, когда больше нет реквизитов. доступны больше.
Рабочий пример можно найти здесь.
Ух ты! @ Оливер, это именно то, что мне нужно. Большое спасибо, что нашли время и предоставили такое комплексное решение.
Рад слышать. Тем не менее, вам, вероятно, следует реализовать упомянутые улучшения, особенно если вы вызываете эту вещь очень часто, в циклах или с большим количеством элементов в исходном коде.
Верно подмечено. Спасибо еще раз.
Какие типы свойств должны поддерживаться (например, строка, int, DateTime, классы). Что в вашем примере с идентификатором свойства? Как метод узнает, какие свойства следует сравнивать?