Ответ 1
Это можно сделать, но делать это правильно будет довольно сложно (и это определенно не поместится в моем ответе). Следующая простая реализация предполагает, что ваш объект имеет только свойства чтения и записи и конструктор без параметров:
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
Это немного поражает точку, потому что вы, вероятно, захотите использовать ее для неизменяемых типов, но тогда вам всегда нужно вызывать конструктор со всеми аргументами, и неясно, как связать параметры конструктора (когда вы создаете экземпляр) со свойствами, которые вы можете прочитать.
Метод With
создает новый экземпляр, копирует все значения свойств и затем устанавливает тот, который вы хотите изменить (используя PropertyInfo
, извлеченный из дерева выражений, без каких-либо проверок!)
public static T With<T, P>(this T self, Expression<Func<T, P>> selector, P newValue)
{
var me = (MemberExpression)selector.Body;
var changedProp = (System.Reflection.PropertyInfo)me.Member;
var clone = Activator.CreateInstance<T>();
foreach (var prop in typeof(T).GetProperties())
prop.SetValue(clone, prop.GetValue(self));
changedProp.SetValue(clone, newValue);
return clone;
}
Следующая демонстрация ведет себя так, как ожидалось, но, как я уже сказал, она имеет множество ограничений:
var person = new Person() { Name = "Tomas", Age = 1 };
var newPerson = person.With(p => p.Age, 20);
В общем, я думаю, что использование универсального метода на основе отражения, такого как With
, здесь может быть не такой хорошей идеей, если у вас нет времени для его правильного использования. Может быть проще просто реализовать один метод With
для каждого используемого вами типа, который принимает необязательные параметры и устанавливает их значения в клонированное значение (созданное вручную), если значение не равно null
. Подпись будет выглядеть примерно так:
public Person With(string name=null, int? age=null) { ... }