I asked a question yesterday regarding using either reflection or Strategy Pattern for dynamically calling methods.
However, since then I have decided to change the methods into individual classes that implement a common interface. The reason being, each class, whilst bearing some similarities also perform certain methods unique to that class.
I had been using a strategy as such:
switch (method) { case "Pivot": return new Pivot(originalData); case "GroupBy": return new GroupBy(originalData); case "Standard deviation": return new StandardDeviation(originalData); case "% phospho PRAS Protein": return new PhosphoPRASPercentage(originalData); case "AveragePPPperTreatment": return new AveragePPPperTreatment(originalData); case "AvgPPPNControl": return new AvgPPPNControl(originalData); case "PercentageInhibition": return new PercentageInhibition(originalData); default: throw new Exception("ERROR: Method " + method + " does not exist."); }
However, as the number of potential classes grow, I will need to keep adding new ones, thus breaking the closed for modification rule.
Instead, I have used a solution as such:
var test = Activator.CreateInstance(null, "MBDDXDataViews."+ _class); ICalculation instance = (ICalculation)test.Unwrap(); return instance;
Effectively, the _class parameter is the name of the class passed in at runtime. Is this a common way to do this, will there be any performance issues with this?
I am fairly new to reflection, so your advice would be welcome.
When using reflection you should ask yourself a couple of questions first, because you may end up in an over-the-top complex solution that's hard to maintain:
dynamic
invocations (only .NET 4.0 and above)?From your description I assume you do not know the types at compile time, you only know they share the interface ICalculation
. If this is correct, then number (1) and (2) above are likely not possible in your scenario.
This is an important question to ask. The overhead of using reflection can impede a more than 400-fold penalty: that slows down even a moderate amount of calls.
The resolution is relatively easy: instead of using Activator.CreateInstance
, use a factory method (you already have that), look up the MethodInfo
create a delegate, cache it and use the delegate from then on. This yields only a penalty on the first invocation, subsequent invocations have near-native performance.
A lot is possible here, but I'd really need to know more of your situation to assist in this direction. Often, I end up combining dynamic
with generics, with cached reflection. When using information hiding (as is normal in OOP), you may end up with a fast, stable and still well-extensible solution.
Of the five questions, this is perhaps the most important one to worry about. It is very important to create your own exceptions that give clear information about reflection mistakes. That means: every call to a method, constructor or property based on an input string or otherwise unchecked information must be wrapped in a try/catch. Catch only specific exceptions (as always, I mean: never catch Exception
itself).
Focus on TargetException
(method does not exist), TargetInvocationException
(method exists, but rose an exc. when invoked), TargetParameterCountException
, MethodAccessException
(not the right privileges, happens a lot in ASP.NET), InvalidOperationException
(happens with generic types). You don't always need to try to catch all of them, it depends on the expected input and expected target objects.
Get rid of your Activator.CreateInstance
and use MethodInfo to find the factory-create method, and use Delegate.CreateDelegate
to create and cache the delegate. Simply store it in a static Dictionary
where the key is equal to the class-string in your example code. Below is a quick but not-so-dirty way of doing this safely and without losing too much type safety.
public class TestDynamicFactory { // static storage private static Dictionary<string, Func<ICalculate>> InstanceCreateCache = new Dictionary<string, Func<ICalculate>>(); // how to invoke it static int Main() { // invoke it, this is lightning fast and the first-time cache will be arranged // also, no need to give the full method anymore, just the classname, as we // use an interface for the rest. Almost full type safety! ICalculate instanceOfCalculator = this.CreateCachableICalculate("RandomNumber"); int result = instanceOfCalculator.ExecuteCalculation(); } // searches for the class, initiates it (calls factory method) and returns the instance // TODO: add a lot of error handling! ICalculate CreateCachableICalculate(string className) { if(!InstanceCreateCache.ContainsKey(className)) { // get the type (several ways exist, this is an eays one) Type type = TypeDelegator.GetType("TestDynamicFactory." + className); // NOTE: this can be tempting, but do NOT use the following, because you cannot // create a delegate from a ctor and will loose many performance benefits //ConstructorInfo constructorInfo = type.GetConstructor(Type.EmptyTypes); // works with public instance/static methods MethodInfo mi = type.GetMethod("Create"); // the "magic", turn it into a delegate var createInstanceDelegate = (Func<ICalculate>) Delegate.CreateDelegate(typeof (Func<ICalculate>), mi); // store for future reference InstanceCreateCache.Add(className, createInstanceDelegate); } return InstanceCreateCache[className].Invoke(); } } // example of your ICalculate interface public interface ICalculate { void Initialize(); int ExecuteCalculation(); } // example of an ICalculate class public class RandomNumber : ICalculate { private static Random _random; public static RandomNumber Create() { var random = new RandomNumber(); random.Initialize(); return random; } public void Initialize() { _random = new Random(DateTime.Now.Millisecond); } public int ExecuteCalculation() { return _random.Next(); } }
I suggest you give your factory implementation a method RegisterImplementation
. So every new class is just a call to that method and you are not changing your factories code.
UPDATE:
What I mean is something like this:
Create an interface that defines a calculation. According to your code, you already did this. For the sake of being complete, I am going to use the following interface in the rest of my answer:
public interface ICalculation { void Initialize(string originalData); void DoWork(); }
Your factory will look something like this:
public class CalculationFactory { private readonly Dictionary<string, Func<string, ICalculation>> _calculations = new Dictionary<string, Func<string, ICalculation>>(); public void RegisterCalculation<T>(string method) where T : ICalculation, new() { _calculations.Add(method, originalData => { var calculation = new T(); calculation.Initialize(originalData); return calculation; }); } public ICalculation CreateInstance(string method, string originalData) { return _calculations[method](originalData); } }
This simple factory class is lacking error checking for the reason of simplicity.
UPDATE 2:
You would initialize it like this somewhere in your applications initialization routine:
CalculationFactory _factory = new CalculationFactory(); public void RegisterCalculations() { _factory.RegisterCalculation<Pivot>("Pivot"); _factory.RegisterCalculation<GroupBy>("GroupBy"); _factory.RegisterCalculation<StandardDeviation>("Standard deviation"); _factory.RegisterCalculation<PhosphoPRASPercentage>("% phospho PRAS Protein"); _factory.RegisterCalculation<AveragePPPperTreatment>("AveragePPPperTreatment"); _factory.RegisterCalculation<AvgPPPNControl>("AvgPPPNControl"); _factory.RegisterCalculation<PercentageInhibition>("PercentageInhibition"); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With