OperationBuilders

Purpose

Operation builders are the low-level primitives in Feber for generating per-property Expression fragments. They provide the foundation for higher-level builders like ActionBuilder and FunctionBuilder, which consume OperationExpressions to compile complete delegates.

Why Use This Approach?

This builder pattern is designed to improve runtime performance and developer productivity for repeated property-based operations. By automating the generation of property-based logic, you eliminate repetitive boilerplate code and avoid the overhead of reflection and manual expression construction on every invocation. The trade-off is a small upfront cost for building and caching the operation, which is quickly amortized when the operation is used many times.

  • Performance: Compiled delegates execute much faster than repeated reflection or dynamic code generation.
  • Scalability: The cost of building the operation is paid only once per type; subsequent invocations are as fast as a direct delegate call.
  • Developer Productivity: This approach reduces code-bloat and improves developer productivity by removing repetitive property-handling logic from your codebase. Instead, you automate the generation of these operations, making your codebase cleaner, easier to maintain, and less error-prone.

Architectural Role

  • Scans prototype types for public instance properties using SwissKnife helpers.
  • Exposes FilteredProperties and OperationExpressions for property-based expression generation.
  • Supplies stable ParameterExpression instances for lambda compilation.
  • Used as the base for custom builders that need fine-grained control over property expression generation.

Core Types & Behaviors

  • OperationBuilderBase<TProto>: Scans properties, exposes FilteredProperties and OperationExpressions, and expects implementors to override BuildPropertyExpression.
  • UnaryOperationBuilderBase<TProto, TInstance>: Supplies a stable InstanceParameterExpression and expects implementors to override PropertyExpression(PropertyInfo, ParameterExpression).
  • BinaryOperationBuilderBase<TProto, TLeftInstance, TRightInstance>: Supplies LeftInstanceParameterExpression and RightInstanceParameterExpression and expects implementors to override PropertyExpression(PropertyInfo, ParameterExpression, ParameterExpression).

Usage Example: Null-Check Builder

using System;
using System.Linq.Expressions;
using System.Reflection;
using BrightSword.Feber.Core;

public sealed class NullCheckBuilder<T> : UnaryOperationBuilderBase<T, T>
{
    protected override Expression PropertyExpression(PropertyInfo propertyInfo, ParameterExpression instanceParameter)
    {
        var propertyAccess = Expression.Property(instanceParameter, propertyInfo);
        var nullConstant = Expression.Constant(null, propertyInfo.PropertyType);
        return Expression.Equal(propertyAccess, nullConstant);
    }

    public Expression BuildAll() => Expression.Block(OperationExpressions);
}

// Usage
// var builder = new NullCheckBuilder<MyType>();
// var block = builder.BuildAll();
// var lam = Expression.Lambda<Action<MyType>>(block, builder.InstanceParameterExpression);
// var action = lam.Compile();

Best Practices

  • ParameterExpression identity: Always use the builder’s provided parameter expressions. Do not create new ones inside overrides.
  • Property scanning: Use SwissKnife’s GetAllProperties for robust property discovery.
  • Delegate composition: Higher-level builders (ActionBuilder/FunctionBuilder) consume OperationExpressions to compile delegates.

Customization

  • Override BuildPropertyExpression or the more specialized property expression methods in unary/binary builders for custom logic.
  • Use the protected properties for parameter expressions to ensure correct lambda compilation.

See Also


BrightSword © 2025. Distributed under CC BY 4.0.