A Disgusting Hack (factory method on base class that uses reflection)
This is a dirty thing to do, and I feel dirty for doing it:
public abstract class InterestRate {
// irrelevant details
public static T ImpliedRate<T>(
double factor,
double time,
DayCounter dayCounter
) where T : NonCompoundedInterestRate {
MethodInfo methodInfo = typeof(T).GetMethod(
"ImpliedRate",
BindingFlags.Static);
return (T)methodInfo.Invoke(
null,
new object[] { factor, time, dayCounter }
);
}
public static T ImpliedRate<T>(
double factor,
double time,
DayCounter dayCounter,
Frequency frequency
) where T : CompoundedInterestRate {
MethodInfo methodInfo = typeof(T).GetMethod(
"ImpliedRate",
BindingFlags.Static);
return (T)methodInfo.Invoke(
null,
new object[] { factor, time, dayCounter, frequency }
);
}
Here I have classes NonCompoundedInterestRate
(abstract) and CompoundedInterestRate
deriving from abstract class InterestRate
. I have several a couple concrete implementations of NonCompoundedInterestRate
that have static methods named ImpliedRate
with the appropriate signature for the above reflection to work.
开发者_如何转开发Using reflection to call a static method that is not even guaranteed to be there on a derived class just reeks. Is there a better way to handle this?
You should feel dirty. Here's some soap:
public static class InterestRateFactories
{
static InterestRateFactories()
{
_factories = new List<IInterestRateFactory>();
// register default factories, although you can also register them elsewhere, like in your ioc setup
}
private static readonly List<IInterestRateFactory> _factories;
public static void RegisterFactory(IInterestRateFactory factory)
{
_factories.Add(factory);
}
public static T ImpliedRate<T>(double factor, double time, DayCounter dayCounter)
where T : NonCompoundedInterestRate
{
var factory = _factories.FirstOrDefault(x => x.CanCreate(typeof(T), false));
if (factory == null)
{
throw new NotSupportedException("Cannot create a non-compounded implied interest rate of type " + typeof(T).Name);
}
return (T)factory.Create(factor, time, dayCounter);
}
public static T ImpliedRate<T>(double factor, double time, DayCounter dayCounter, Frequency frequency)
where T : CompoundedInterestRate
{
var factory = _factories.FirstOrDefault(x => x.CanCreate(typeof(T), false));
if (factory == null)
{
throw new NotSupportedException("Cannot create a compounded implied interest rate of type " + typeof(T).Name);
}
return (T)factory.Create(factor, time, dayCounter, frequency);
}
}
public interface IInterestRateFactory
{
bool CanCreate(Type nonCompoundedInterestRateType, bool compounded);
NonCompoundedInterestRate Create(double factor, double time, DayCounter dayCounter);
CompoundInterestRate Create(double factor, double time, DayCounter dayCounter, Frequency frequency);
}
Seems like the caller could just as easily call the factory method on the derived class as call this method passing the derived type as T.
A slightly more explicit contract here would be to add a new() constraint to T, call the default ctor, then call an Init abstract method defined on the base class.
The factory pattern has a testability advantage, but not as you've used it here. A third alternative would be to have the caller pass an instance of a factory class to use (the ImpliedRate method would be on the factory interface). This would be handy for unit testing but perhaps onerous for the consumer of the API.
Instead of static methods you can use normal methods and something like modified Clone/Prototype pattern. For example:
public static class InstanceMap
{
private static readonly Dictionary<Type,object> instances =
new Dictionary<Type,object>();
public static void AddInstance(object instance)
{
instances[instance.GetType()] = instance;
}
public static T GetInstance<T>() { return (T) instances[typeof(T)]; }
}
public interface INonCompoundedInterestRate
{
INonCompoundedInterestRate ImpliedRate(double factor,
double time,
DayCounter dayCounter);
}
public class MyNonCompoundedInterestRate: INonCompoundedInterestRate
{
public INonCompoundedInterestRate ImpliedRate(double factor,
double time,
DayCounter dayCounter) { /* do smth*/ }
static MyNonCompoundedInterestRate()
{
InstanceMap.AddInstance(new MyNonCompoundedInterestRate());
}
}
public abstract class InterestRate {
public static T ImpliedRate<T>(
double factor,
double time,
DayCounter dayCounter
) where T : INonCompoundedInterestRate
{
return InstanceMap.GetInstance<T>().
ImpliedRate(factor, time, dayCounter);
}
// ...
}
In my experience you can only instantiate a parameter less constructor of a generic.
What you are trying to achieve can only be done with reflection.
You're always going to have conflicts trying to mix static (factory) methods with inheritance. It's difficult to get the polymorphic behavior you're looking for. I had a similar problem, and am currently using reflection. The other option, as already mentioned, is to not use static methods if it's not required. Then you might be able to use a template method, or any other strategy that will work well with inheritance.
Any reason not to just define those on a non-generic interface that's declared on the generic bases, then do a cast to T? I don't see any generic args being passed...
精彩评论