8

I'm a bit of a novice with Reflection. I'm hoping that it's possible to do what I'd like it to. I've been working through ProjectEuler to learn the language, and I have a base class called Problem. Every individual PE problem is a separate class, i.e. Problem16. To run my calculations, I use the following code:

using System;
using Euler.Problems;
using Euler.Library;

namespace Euler
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Problem prob = new Problem27();
        }
    }
}

I have completed 50 problems now, and I want to create a loop to run them all. My base class Problem has a method that appends to a text file the problem number, the answer, and the execution time that's called in each class's default constructor. I could manually change the function call for all 50, but as I continue to complete problems, this will end up being a lot of work.

I'd much rather do it programatically. I was hoping for this pseudocode become a reality:

for (int i = 1; i <= 50; i++)
{
    string statement = "Problem prob = new Problem" + i + "();";
    // Execute statement
}
Lee Taylor
  • 7,761
  • 16
  • 33
  • 49
wmaynard
  • 238
  • 1
  • 3
  • 12
  • This is cool. It's probably possible because unit testing systems do something similar. – Oliver Dec 21 '12 at 17:32
  • possible duplicate of [C# How to add a code to execute at Runtime](http://stackoverflow.com/questions/10831421/c-sharp-how-to-add-a-code-to-execute-at-runtime) – Peter Ritchie Dec 21 '12 at 17:36
  • by the way, these problems need sometimes quite a lot of calculation time. so perhaps you dont want to execute every time your complete set of problems – user287107 Dec 21 '12 at 17:39
  • @user287107 My three longest execution times are 26s, 12s, and 0.8s, respectively - my algorithms are fairly solid. I just want to create a text file report without manually recording all the data - as I'm working on the code of future problems, I will only run one problem at a time. – wmaynard Dec 21 '12 at 19:23

5 Answers5

10

with reflections, you can do much nicer things.

for example, declare an interface

interface IEulerProblem 
{
   void SolveProblem();
}

write your classes which are derived from IEulerProblem.

then you can run all within (technically) one nice line of code:

Assembly.GetEntryAssembly()
        .GetTypes()
        .Where(t => typeof(IEulerProblem).IsAssignableFrom(t))
        .Where(t => !t.IsInterface && !t.IsAbstract)
        .Select(t => Activator.CreateInstance(t) as IEulerProblem)
        .OrderBy(t => t.GetType().Name).ToList()
        .ForEach(p => p.SolveProblem());
Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
user287107
  • 9,286
  • 1
  • 31
  • 47
  • 2
    @Oliver Did you know it only takes [three commands to install Gentoo](http://bash.org/?464385)? – Scott Chamberlain Dec 21 '12 at 19:25
  • 1
    Thank you so much for this. This is my first question on SO, and the community here doesn't disappoint. I simplified the code a little bit too, but this gave me what I needed! – wmaynard Dec 21 '12 at 20:29
  • If all Problems derived from Problem, the interface doesn't provide any benefit. Simply casting them `as Problem` solves the same problem with less abstraction. – Erik Philips Dec 21 '12 at 20:45
  • using an interface rather than an abstract class has the advantage, that other base classes can be used, e.g. "Component", "Control", "Form" or so ... – user287107 Dec 21 '12 at 21:05
1

First take a look at Get all inherited classes of an abstract class which applies to non-abstract classes as well.

Then you can simply call the method on the base class for each.

foreach (problem p in problems)
{
  p.MyMethod()
}
Community
  • 1
  • 1
Erik Philips
  • 53,428
  • 11
  • 128
  • 150
  • I started with this answer and cross-referenced it with user 287107's and got my code working great. Thanks for the lead! – wmaynard Dec 21 '12 at 20:34
0

Yes, this is possible, you'll want to read up on MethodInfo.Invoke: http://msdn.microsoft.com/en-us/library/system.reflection.methodinfo.invoke.aspx

mletterle
  • 3,968
  • 1
  • 24
  • 24
0
var problems = Assembly.GetExecutingAssembly().GetTypes()
                       .Where(t => !t.IsAbstract && typeof(Problem).IsAssignableFrom(t));

foreach(var p in problems)
{
    var euler = Activator.CreateInstance(p) as Problem;
    euler.Solve(); // ??
}
Austin Salonen
  • 49,173
  • 15
  • 109
  • 139
0

As one of the possible solutions, I would like to propose to create list of constructor delegates without the use of reflection.
You will still have to populate the list 50 times, but only once. You will have typesafety though and can specify different constructors for each of the derived classes. Something like this:

 List<Func<Test>> tests = new List<Func<Test>>();
            tests.Add(() => new Test1());
            tests.Add(() => new Test2());
            foreach (var constructor in tests)
            {
                Test test = constructor();
            }
RAS
  • 3,375
  • 15
  • 24