Refactoring to avoid repetition in a lambda expression

These two methods demonstrate repetition:

public static Expression<Func<Foo, FooEditDto>> EditDtoSelector() { return f => new FooEditDto { PropertyA = f.PropertyA, PropertyB = f.PropertyB, PropertyC = f.PropertyC, PropertyD = f.PropertyD, PropertyE = f.PropertyE }; } public static Expression<Func<Foo, FooListDto>> ListDtoSelector() { return f => new FooDto { PropertyA = f.PropertyA, PropertyB = f.PropertyB, PropertyC = f.PropertyC }; } 

How can I reorganize to exclude this recurrence?

UPDATE: Oh, I forgot to mention an important point. FooEditDto is a subclass of FooDto.

+6
c # lambda refactoring
source share
4 answers

If FooEditDto is a subclass of FooDto and you don't need MemberInitExpressions expressions, use the constructor:

 class FooDto { public FooDto(Bar a, Bar b, Bar c) { PropertyA = a; PropertyB = b; PropertyC = c; } public Bar PropertyA {get;set;} public Bar PropertyB {get;set;} public Bar PropertyC {get;set;} } class FooEditDto : FooDto { public FooEditDto(Bar a, Bar b, Bar c) : base(a,b,c) public Bar PropertyD {get;set;} public Bar PropertyE {get;set;} } public static Expression<Func<Foo, FooEditDto>> EditDtoSelector() { return f => new FooEditDto(f.PropertyA,f.PropertyB,f.PropertyC) { PropertyD = f.PropertyD, PropertyE = f.PropertyE }; } 
+1
source share

Well, I have a really terrible way to do this.

You can write a method that used reflection (bear with me!) To work out all the properties for a particular type and build a delegate (using Reflection.Emit) to copy properties from that type to another. Then use an anonymous type to make sure that you only need to create a copy delegate once so that it is fast. Then your method will look like this:

 public static Expression<Func<Foo, FooEditDto>> EditDtoSelector() { return f => MagicCopier<FooEditDto>.Copy(new { f.PropertyA, f.PropertyB, f.PropertyC, f.PropertyD, f.PropertyC }); } 

Nuances here:

  • MagicCopier is a generic type, and Copy is a generic method that allows you to explicitly specify a target type, but implicitly specify a source type.
  • It uses a projection initializer to infer property names from expressions used to initialize an anonymous type.

I'm not sure if it's really worth it, but it's a pretty funny idea ... I might have to implement it :)

EDIT: MemberInitExpression we could do all this with an expression tree, which makes it a lot easier than CodeDOM. Will try tonight ...

EDIT: Done, and it's actually pretty simple code. Here's the class:

 /// <summary> /// Generic class which copies to its target type from a source /// type specified in the Copy method. The types are specified /// separately to take advantage of type inference on generic /// method arguments. /// </summary> public static class PropertyCopy<TTarget> where TTarget : class, new() { /// <summary> /// Copies all readable properties from the source to a new instance /// of TTarget. /// </summary> public static TTarget CopyFrom<TSource>(TSource source) where TSource : class { return PropertyCopier<TSource>.Copy(source); } /// <summary> /// Static class to efficiently store the compiled delegate which can /// do the copying. We need a bit of work to ensure that exceptions are /// appropriately propagated, as the exception is generated at type initialization /// time, but we wish it to be thrown as an ArgumentException. /// </summary> private static class PropertyCopier<TSource> where TSource : class { private static readonly Func<TSource, TTarget> copier; private static readonly Exception initializationException; internal static TTarget Copy(TSource source) { if (initializationException != null) { throw initializationException; } if (source == null) { throw new ArgumentNullException("source"); } return copier(source); } static PropertyCopier() { try { copier = BuildCopier(); initializationException = null; } catch (Exception e) { copier = null; initializationException = e; } } private static Func<TSource, TTarget> BuildCopier() { ParameterExpression sourceParameter = Expression.Parameter(typeof(TSource), "source"); var bindings = new List<MemberBinding>(); foreach (PropertyInfo sourceProperty in typeof(TSource).GetProperties()) { if (!sourceProperty.CanRead) { continue; } PropertyInfo targetProperty = typeof(TTarget).GetProperty(sourceProperty.Name); if (targetProperty == null) { throw new ArgumentException("Property " + sourceProperty.Name + " is not present and accessible in " + typeof(TTarget).FullName); } if (!targetProperty.CanWrite) { throw new ArgumentException("Property " + sourceProperty.Name + " is not writable in " + typeof(TTarget).FullName); } if (!targetProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType)) { throw new ArgumentException("Property " + sourceProperty.Name + " has an incompatible type in " + typeof(TTarget).FullName); } bindings.Add(Expression.Bind(targetProperty, Expression.Property(sourceParameter, sourceProperty))); } Expression initializer = Expression.MemberInit(Expression.New(typeof(TTarget)), bindings); return Expression.Lambda<Func<TSource,TTarget>>(initializer, sourceParameter).Compile(); } } 

And calling him:

 TargetType target = PropertyCopy<TargetType>.CopyFrom(new { First="Foo", Second="Bar" }); 
+2
source share

Duplicate in names, but C # doesn't know that PropertyA in one class is associated with PropertyA in another. You must explicitly specify the connection. The way you did it works great. If you had enough of them, you might consider using reflection to write one method that could do this for any pair of classes.

Note the performance implications of any method that you choose. Reflection itself is slower. However, you can also use reflection to emit an IL, which, once it emits, will work as fast as what you wrote. You can also generate an expression tree and convert it to a compiled delegate. These methods are somewhat complicated, so you need to weigh the tradeoffs.

0
source share

You can allow the caller to return their own anonymous type object with only the necessary properties:

 public static Expression<Func<Foo,T>> GetSelector<T>(Expression<Func<Foo,T>> f) { return f; } /* ... */ var expr = GetSelector(f => new{f.PropertyA,f.PropertyB,f.PropertyC}); 
0
source share

All Articles