In the following contrived example, I have a collection of PropertyManager
s that each contains selector and assigner delegates to read from and write to a property on the generic type. It feels clunky for lots of reasons, not least of which is that both delegates are basically there to serve the same purpose: To identify the property on the generic that I want to read from then write back into.
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
namespace Tests
{
[TestFixture]
public class ReadWritePropertySelectorTests
{
public void Round<T>(IEnumerable<T> collection,
params PropertyManager<T>[] propertyManagers)
{
foreach (var item in collection)
{
foreach (var propertyManager in propertyManagers)
{
var valueToRound = propertyManager.Selector(item);
propertyManager.Assigner(item, Math.Round(valueToRound, 1));
}
}
}
public class PropertyManager<T>
{
public Func<T, decimal> Selector { get; set; }
public Action<T, decimal> Assigner { get; set; }
}
public class Combatant
{
public decimal Attack { get; set; }
public decimal Defense { get; set; }
public decimal Health { get; set; }
}
[Test]
public void Test()
{
// Arrange
var combatants = new[]
{
new Combatant { Attack = 33.12M, Defense = 12.771M, Health = 60.1181M }
};
// Act
Round(combatants,
new PropertyManager<Combatant>
{
Selector = c => c.Attack, Assigner = (c, v) => c.Attack = v
},
new PropertyManager<Combatant>
{
Selector = c => c.Defense, Assigner = (c, v) => c.Defense = v
},
new PropertyManager<Combatant>
{
Selector = c => c.Health, Assigner = (c, v) => c.Health = v
});
// Assert
var array = combatants.ToArray();
Assert.AreEqual(33.1M, array[0].Attack);
Assert.AreEqual(12.8M, array[0].Defense);
Assert.AreEqual(60.1M, array[0].Health);
}
}
}
Is there any way to redo this to make the call to Round()
more concise, preferably removing the need for a strongly typed PropertyManager
? I think ultimately calling it as follows would be awesome, but I can't figure out the language constructs to use to get there:
Round(combatants, c => c.Attack, c => c.Defense, c => c.Health);