HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Generic immutable object builder

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
genericimmutableobjectbuilder

Problem

I've made an object builder which can create all sorts of classes, it can also create immutable objects.

There are 2 requirements that must be met in order for this pattern to work with fully immutable objects.

  • The properties that you want to be modified must have a backing readonly field.



  • The backing field must follow some convention in my case it must start with small letter and optionally it can start with _ too, of course this can be extended to match your preferences.



Here is the actual builder class :

```
public class ImmutableObjectBuilder
where T : class
{
private class PropertyWrapper
{
public PropertyInfo Property { get; }
public object Value { get; set; }

internal PropertyWrapper(PropertyInfo property, object value)
{
Property = property;
Value = value;
}
}

private readonly IDictionary propertiesInfo = new Dictionary();

public ImmutableObjectBuilder()
{
PropertyInfo[] properties = typeof(T).GetProperties();
foreach (PropertyInfo p in properties)
{
propertiesInfo.Add(p.Name, new PropertyWrapper(p, null));
}
}

public ImmutableObjectBuilder WithValue(string propertyName, TValue value)
{
if (!propertiesInfo.ContainsKey(propertyName))
{
throw new KeyNotFoundException("The type of TValue is different than the type of T");
}
propertiesInfo[propertyName].Value = value;
return this;
}

public TObject Build(Func instance)
where TObject : T
{
TObject localInstance = instance();
PropertyInfo[] instanceProperties = localInstance.GetType().GetProperties();

PropertyInfo[] matchingProperties =
(from tProperty in instanceProperties
from localProperty in propertiesInfo
where tProperty.Name == localProperty.Key &&
tProperty.PropertyType == localProperty.Value.Property.Pr

Solution

I would prefer the following syntax for its compile-time type-checking:

Employee John = humanBuilder
    .WithValue(t => t.Name, "John")
    .WithValue(t => t.Age, 32)
    .Build();


Enabled by the following version of WithValue:

public ImmutableObjectBuilder WithValue(
       Expression> property, TValue value)
{
    var body = property.Body as MemberExpression;
    if (body == null)
    {
        throw new InvalidOperationException("Improperly formatted expression");
    }
    var propertyName = body.Member.Name;
    propertiesInfo[propertyName].Value = value;
    return this;
}


There is a requirement that the expression is of the form t => t.SimplePropertyAccess which cannot be enforced at compile-time, but at least you do get intellisense and compile-time type-checking.

Code Snippets

Employee John = humanBuilder
    .WithValue(t => t.Name, "John")
    .WithValue(t => t.Age, 32)
    .Build<Employee>();
public ImmutableObjectBuilder<T> WithValue<TValue>(
       Expression<Func<T, TValue>> property, TValue value)
{
    var body = property.Body as MemberExpression;
    if (body == null)
    {
        throw new InvalidOperationException("Improperly formatted expression");
    }
    var propertyName = body.Member.Name;
    propertiesInfo[propertyName].Value = value;
    return this;
}

Context

StackExchange Code Review Q#151078, answer score: 8

Revisions (0)

No revisions yet.