Whenever you can’t come up with a good specification.
I’m currently working on an application that uses Dto’s as a way of communicating and I previously demonstrated how easy it was to use quicknet to test the mapping from and to business object.
The input.Equals(output) part only works ofcourse if the Dto’s are implemented as ValueObjects. Doing this is a valid design choice anyway, but it does imply some extra coding. C# could really do with something like an ‘immutable’ keyword I believe. Or maybe allow the access to the private setters of a property in Initializers.
Anyway, back on topic. In order to reduce some of this repetitive coding, I coded an immutable ‘SelectionList’ that can be used by the the Gui. It has an inner list of key/value elements and a method for selecting the current item. Selecting something however returns a copy of the object, the object itself is not modified.
Now I couldn’t really figure out how to test this with quicknet. I really want to test the Equals method. Writing a spec for this would just repeat the code inside the SelectionList and would be quite complicated.
I ended up just TDD-ing it using Facts instead of Specs.
I’m quite happy with the result so here’s the code for those of you that would like an immutable dictionary type thing, but still allows you to set a selection.
// Implemented as a value object.
// - Immutable : manipulating the data gives you a new instance.
// - Value Equality : based on the Field values, not the reference, or Field references.
// Both qualities above should always go together as otherwise the objects
// can not be safely used in any kind of HashTable, which includes dictionaries and the likes.
// The dictionary itself is only copied when exposed, in any other case it remains referentially equal.
// Bit of a FlyWeight pattern.
public class SelectionList<TKey, TValue>
{
private List<KeyValuePair<TKey, TValue>> dictionary;
public TKey Selection { get; private set; }
public TValue SelectionValue { get; private set; }
//private constructors, use the factory method below
private SelectionList() { }
private SelectionList(
List<KeyValuePair<TKey, TValue>> dictionary,
TKey selection,
TValue selectionValue)
{
this.dictionary = dictionary;
Selection = selection;
SelectionValue = selectionValue;
}
// Factory Method.
// Takes
// - a collection of elements.
// - a memberexpression defining the field (of the elements in the list) to use as key.
// - a memberexpression defining the field to use as value.
// - an initial selection.
// passing in an invalid initial selection puts the object in an unstable state, see the tests.
public static SelectionList<TKey, TValue> New<T>(
IEnumerable<T> enumerable,
Expression<Func<T, TKey>> keyExpression,
Expression<Func<T, TValue>> valueExpression,
TKey initialSelection)
{
SelectionList<TKey, TValue> list =
new SelectionList<TKey, TValue>
{
dictionary = new List<KeyValuePair<TKey, TValue>>()
};
Func<T, TKey> keyFunc = keyExpression.Compile();
Func<T, TValue> valueFunc = valueExpression.Compile();
enumerable.ForEach(
element => list.dictionary.Add(
new KeyValuePair<TKey, TValue>(
keyFunc.Invoke(element),
valueFunc.Invoke(element))));
return list.Select(initialSelection);
}
// The only way to manipulate the object.
// Trying to select something that isn't there simply returns a copy of the object.
// When correctly selecting something it returns a copy of the object
// with the new selection set.
public SelectionList<TKey, TValue> Select(TKey key)
{
if (!dictionary.Exists(p => p.Key.Equals(key)))
return new SelectionList<TKey, TValue>(dictionary, Selection, SelectionValue);
KeyValuePair<TKey, TValue> pair = dictionary.Find(p => p.Key.Equals(key));
return new SelectionList<TKey, TValue>(dictionary, pair.Key, pair.Value);
}
// Returns a copy of the private collection.
// Primarely used by Gui to display combo's and the likes.
// It exposes inner state but the inner state can't be changed.
// Using the implicit conversion technique as syntactic sugar,
// meaning we can just assign a selection list to a variable of type
// List<KeyValuePair<TKey, TValue>> without an extra call.
public static implicit operator List<KeyValuePair<TKey, TValue>>(SelectionList<TKey, TValue> aSelectionList)
{
return new List<KeyValuePair<TKey, TValue>>(aSelectionList.dictionary);
}
// Equality member.
public bool Equals(SelectionList<TKey, TValue> other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return
dictionary.SequenceEqual(other.dictionary) &&
Equals(other.Selection, Selection) && Equals(other.SelectionValue, SelectionValue);
}
// Equality member.
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof(SelectionList<TKey, TValue>)) return false;
return Equals((SelectionList<TKey, TValue>)obj);
}
// Equality member.
private int GetDictionaryHashCode()
{
int result = 0;
dictionary.ForEach(
kv =>
{
result = (result * 397) ^ kv.Key.GetHashCode();
result = (result * 397) ^ kv.Value.GetHashCode();
});
return result;
}
// Equality member.
public override int GetHashCode()
{
unchecked
{
int result = dictionary != null ? GetDictionaryHashCode() : 0;
result = (result * 397) ^ Selection.GetHashCode();
result = (result * 397) ^ SelectionValue.GetHashCode();
return result;
}
}
}
public static class SelectionList
{
// Helper Factory Method that gets rid of some of the generics.
public static SelectionList<TKey, TValue> New<T, TKey, TValue>(
IEnumerable<T> enumerable,
Expression<Func<T, TKey>> keyExpression,
Expression<Func<T, TValue>> valueExpression,
TKey initialSelection)
{
return SelectionList<TKey, TValue>.New(enumerable, keyExpression, valueExpression, initialSelection);
}
}
And an example of usage :
[Fact]
public void Selecting()
{
List<Element> elements = new List<Element>();
for (int i = 0; i < 5; i++)
{
elements.Add(new Element { Id = i, Display = i.ToString() });
}
SelectionList<int, string> selectionList =
SelectionList.New(elements, el => el.Id, el => el.Display, 0);
selectionList = selectionList.Select(4);
Assert.Equal(4, selectionList.Selection);
Assert.Equal("4", selectionList.SelectionValue);
}
There’s one known issue with this code : If the initial selection parameter of the factory method is not in the collection it puts the object in an invalid state, as the following test demonstrates :
[Fact]
public void BadInitializationThrowsExceptionOnGetHashCode()
{
// Todo : maybe change this to BadInitialization Throws InvalidOperationException ?
List<Element> elements = new List<Element>();
for (int i = 0; i < 5; i++)
{
elements.Add(new Element { Id = i, Display = i.ToString() });
}
SelectionList<int, string> selectionList =
SelectionList.New(elements, t => t.Id, t => t.Display, 7); // Throw exception here ?
Assert.Throws<NullReferenceException>(() => selectionList.GetHashCode());
}
You might, as suggested in the comment validate upon construction and throw an exception there, I haven’t made up my mind yet about this.
Although the class is implemented in just about 140 lines (lengthy comments and all), there’s almost 450 lines of test code behind this, needed to cover all cases. I might still try to come up with some decent specs at a later date.