2016-12-14 17 views
1

У меня есть неизменный объект. Например, простой пример ниже.Как сопоставить свойства объекта с аргументами конструктора в C# для построения автоматической копии

class Person { 
    public string Name {get;} 
    public int Age {get;} 
    public Person(string name, int age){ 
     Name = name; 
     Age = age; 
    } 
} 

Теперь я хотел бы иметь общий метод расширения, такие как

public static class ObjectExtensions { 
    public void T With<T,P>(this T target, Expression<Func<T,P>> selector, P value){ 
     /* some implementatation */ 
    } 
} 

Так что я могу сделать

var person = new Person("brad", 12).With(p=>p.Age,55); 
person.Age.Should().Be(55); 

При использовании метода следует использовать отражение для сопоставления имен аргумента конструктора с свойства существующего объекта.

  • Производительность не является проблемой.
  • Ошибка выполнения в порядке, если имена аргументов конструктора не соответствуют именам свойств. (Анализатор roslyn мог решить эту проблему)
  • Использование отражения для установки свойств с использованием частных сеттеров после мелкого клонирования также нежелательно. (У меня есть решение в настоящее время, которое делает это)

Цель состоит в том, чтобы дополнительные классы не добавлялись к неизменяемому классу, кроме конструктора, с аргументами с именами, которые соответствуют свойствам.

Явный При использовании метода с обнуляемым аргументы, такие как

class Person { 
    public string Name {get;} 
    public int Age {get;} 
    public Person(string name, int age){ 
     Name = name; 
     Age = age; 
    } 
    public Person With(string? name, int? age){ 
     return new Person(name ?? this.Name, age ?? this.Age); 
    } 

} 

хотя элегантный не решение, которое я ищу для моего случая использования.

+0

В этом scenerio вы передаете 'p.Age', но конструктор arg для' Age ''не существует. Вы предполагаете, что имена аргументов contructor соответствуют именам свойств, которые относятся к делу, является ли это безопасным предположением. –

+0

Да, не считая случая, он безопасен. – bradgonesurfing

ответ

1

Вот один пример. Вероятно, это может улучшить кэширование информации о типе для скорости, но это базовый уровень, который вы можете улучшить. Как я упоминал в комментарии, это предполагает, что конструктор имеет параметр с именем так же, как и свойство, игнорируя регистр. Он также предполагает, что есть только один конструктор, но вы можете обновить его, как вам заблагорассудится.

public static T With<T, P>(this T target, Expression<Func<T, P>> selector, P value) 
{ 
    var expression = selector.Body as MemberExpression; 

    if (expression == null) 
    { 
     throw new InvalidOperationException(); 
    } 

    var name = expression.Member.Name; 

    var constructor = typeof(T).GetConstructors().First(); 
    var args = GetParamaters(target, value, constructor, name); 

    return (T)Activator.CreateInstance(typeof(T), args.ToArray()); 
} 

private static IEnumerable<object> GetParamaters<T, P>(T target, P value, ConstructorInfo constructor, string name) 
{ 
    foreach (var parameterInfo in constructor.GetParameters()) 
    { 
     if (parameterInfo.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) 
     { 
      yield return value; 
     } 
     else 
     { 
      var property = 
       typeof(T).GetProperties() 
        .First(x => x.Name.Equals(parameterInfo.Name, StringComparison.InvariantCultureIgnoreCase)); 
      yield return property.GetValue(target, null); 
     } 
    } 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^