Skip to content

Add IPropertyHandlerProvider concept to allow for custom property handling #45

@carusology

Description

@carusology

Background

Right now, all of the ways that IPropertyHandler implementations can be defined is hard-coded into the static constructor of ImmutableBase<TImmutable>. This sucks for two big reasons:

  1. If someone wants to add a custom IPropertyHandler implementation, the have to do it with hackery and reflection. It is essentially non-extensible.
  2. If we add any complexity on how an IPropertyHandler is to be constructed (like, for example, as defined in Add [StringComparison]-like annotation for String properties #12 and Add [EnumerableComparison]-like annotation for IEnumerable properties #14), that logic gets stuck inside of ImmutableBase<TImmutable> when it could have its complexity compartmentalized.

Both of these could be solved with a few interfaces with default implementations.

Task

  • Create two new interfaces, as follows:

    public interface IPropertyHandlerFactory {
        IPropertyHandler Create(PropertyInfo property);
    }
    
    public interface IPropertyHandlerProvider : IPropertyHandlerFactory {
        bool IsSupported(PropertyInfo propertyInfo);
    }
  • Create three implementations, StringPropertyHandlerProvider, EnumerablePropertyHandlerProvider (for Set & List) and DefaultPropertyHandlerProvider, that inspect the relevant PropertyInfo as we do in ImmutableBase<TImmutable> static constructor today to determine support and desired approach to hydration.

  • Create an AggregatePropertyHandlerProvider that takes in a list of IPropertyHandlerProvider implementations and processes them in order. The first match is used to create an IPropertyHandler.

  • Use a IPropertyHandlerFactory in the ImmutableBase<TImmutable> static constructor to hydrate IPropertyHandler implementations instead of doing it all in a hard-coded fashion as it does today. It should have a public getter and setter such that others can change how properties are cached. Additionally, when it changes its value, that should cause all of the cached IPropertyHandler implementations to be rebuilt using the new IPropertyHandlerFactory.

Assuming all of these changes went through, I would expect the static constructor for ImmutableBase<TImmutable> to start to look something like this:

public static IPropertyHandlerFactory PropertyHandlerFactory { 
    get { return handlerFactory; }
    set { handlerFactory = value; ClearHandlersCache(); }
}

static ImmutableBase() {
    handlerFactory = new AggregatePropertyHandlerProvider(
        new StringPropertyHandlerProvider(),
        new EnumerablePropertyHandlerProvider(),
        new DefaultPropertyHandlerProvider()
    );

    // ... other initialization code.
}

Then, when we implement #12 and #14, we can keep that logic in the IPropertyHandlerProvider implementations instead of keeping it centrally located in the ImmutableBase<TImmutable> static constructor.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions