throw new NotImplementedException();
And, it's feasible for a non-abstract class.
The idea is for a base class to require a value via its constructor(s) rather than requiring it via abstract property. Obviously, this is not universally advisable. But I find I have at least one rather substantial object hierarcy in most apps: say, one for persistable objects, maybe another for DTOs, and if a UI is involved, often several flavors of controls or containers, plus architectural base types if we're doing MVVM or such. Often, there's something I'd like to code out once, in a base class, with slight variations for each leaf. There's usually a generic type parameter or two that expresses the type-specific variations, but sometimes there's additional metadata, staticly known to each leaf but not knowable in the general case up at the base. It's the sort of thing where a VB programmer would write an enum into the base class and ride to putative glory on a switch statement. Not so great for framework programming or for, really, any code that shouldn't make you want to fire someone.
In the few hours since I really noticed this and have been salting it around my current project before deciding to blog it, contract via constructor is particularly useful if the property you seek is metadata about each subtype, e.g., so that you can handle it universally in the base type.
For example, suppose I want to specialize
IntBindingPair
into a family of types, each associated with an IEntity<int>
. (An IEntity<int>
here is a persistable object with an int
primary key, and a string property suitable for display. The "binding pair", therefore, is basically a key/value pair for UI binding.) My base class's constructor takes an int
and a string
. I want each new subtype to be constructable from just the IEntity
reference, so I need each subtype to specify how to choose its string
value. (No, it's not time to override ToString()
. Go sit in the corner.)I could use an abstract property:
public abstract Func<TEntity, string> ValueExtractor { get; }
But I'd still be stuck on how to map my child class's sole
IEntity
argument to my base class's int
and string
; the instance property isn't available to be used in constructor args, for obvious reasons.So, when in need, inject a fix! (This is a more commendable motto in software design than in, say, intravenous drug use.)
public abstract class IntBindingPair<TEntity> : IntBindingPair
where TEntity : class, IEntity<int>
{
protected IntBindingPair(TEntity entity, Func<TEntity, string> valueExtractor)
: base(entity.PrimaryKey1, valueExtractor.Invoke(entity))
{
ValueExtractor = valueExtractor;
}
public Func<TEntity, string> ValueExtractor { get; private set; }
}
Now any child type must provide a real (or at least non-null) value for ValueExtractor; otherwise it won't even instantiate.
public class FooDisplay : IntBindingPair<IFoo>
{
public FooDisplay(IFoo foo)
: base(foo, f => f.Bar)
{}
}
public interface IFoo : IEntity<int>
{
string Bar { get; }
}
This same syntax solves another, possibly more general problem: how to transform constructor signatures from child types to suit the requirements of a base type.
No comments:
Post a Comment