views:

340

answers:

5

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?

A: 

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...

nitzmahone
Say I have a generic class `Blah<T>` and in some instance method in `Blah` I invoke `InterestRate.ImpliedRate<T>(factor, time, dayCounter)`. This is where I would use the generic factory method on the base class `InterestRate`.
Jason
+1  A: 

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.

alexdej
Your first suggestion isn't really an option as I have generics `Blah<T>` and in instance methods in `Blah` I invoke `InterestRate.ImpliedRate<T>`. The second appeals to me and I had considered it, but it also seems kind of hackish. In this solution I would be creating two instances of an object, one a throwaway used to create the keeper. What if object creation were expensive? And I agree, the third suggestion is too onerous to use.
Jason
I see. To be clear about my second suggestion: it was to create abstract void Initialize() instance methods on the two base classes that take the same args as the current factory methods. These wouldn't be factory methods, so you wouldn't be creating throwaway instances.
alexdej
+1  A: 

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);
    }
    // ...
}
Dmitry
+1  A: 

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.

Tim Jarvis
+1  A: 

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.

Samuel Meacham