tags:

views:

415

answers:

3

I might be going in the wrong direction, so let me try to sort out my thoughts (and hopefully get some tips from you guys):

Imagine an enum:

public enum ReportType
{
   Performance,
   Trending,
   Statistical
}

This enum will also be a property or paramter to the constructor of a form. The value of the ReportType will determine things like:

a) the text displayed at the top of the form
b) Which controls are visible
c) the text for a combobox <---!!!! this is where the 2nd enum comes in

In regards to the 2nd enum, if it's a Performance ReportType, I would want:

public enum PerformanceGraph
{
    Bar,
    Line,
    Pie,
    Area
}

if it was Trending, I would want:

public enum TrendingGraph
{
    Bar,
    Line
}

This form is just gathering user input. I mean, I'd rather not get into some elaborate inheritance structure for a simple form. Seems like a lot of effort (I could be wrong) when I can quickly do something like:

(imagine this constructor)

public ReportInputForm(ReportType RptType)
{
    m_RptType = RptType;

    if (RptType == ReportType.Performance)
    {
        this.Text = "Performance Title";
        this.CheckBoxCtl.Visible = false;
        this.GraphCombo.Items.AddRange(Enum.GetNames(typeof(PerformanceGraph)));
        this.GraphCombo.SelectedIndex = (int)PerformanceGraph.Bar;
    }
    else if (RptType == ReportType.Trending)
    {
         // blah, blah
    }
    else if (RptType == ReportType.Statistical)
    {
        // blah, blah
    }
    else
    {
        throw new ArgumentException("Invalid ReportType enum value");
    }
}

It starts to get hairy when you want to actually do something with the GraphCombo, because now you need to know which ReportType it is for casting purposes.

Also, going back to my constructor, let's say I want to send it a Graph Value so we can set the GraphCombo's SelectedIndex (this may be the value that the user last selected, as opposed to setting some type of default). Ummm ....

 public ReportInputForm(ReportType RptType, ???WhichGraphEnum???)
 {
 }

I mean, there's three graph enum's right now, because there are three ReportType's. Plus, imagine adding a few more ReportType's.

I suppose I can use an int/long:

 public ReportInputForm(ReportType RptType, int lastGraphType)
 {
 }

But I suppose I'm trying to be Mr. Enum here with my compile-time checking. In other-words, I can detect an error if I could use some type of enum (like so):

ReportInputForm foo = new ReportInputForm(ReportType.Trending, GraphType.Area);

As opposed to:

// Is 4 even a valid graph type for the trending report?  Answer: No
ReportInputForm foo = new ReportInputForm(ReportType.Trending, 4);

I've rambled on enough. Maybe this is more of a design question rather than enum related. I'm just looking for some thoughts on how to approach this. Thanks!

################################################################################## ################################################################################## ######################## EDIT BEGINS HERE ######################################## ################################################################################## ##################################################################################

This is in response to using Generics (I apologize if I'm a bit slow; Thank you Daniel for your response):

Here was a suggestion:

ReportInputForm<TrendingReport> = new ReportInputForm<TrendingReport>(GraphType.Area);

OK, so if I'm to use Generics (and with a Form), I have to create a Type. I'm imagining something like this (we'll only expose one function, setting the title, for now):

public abstract class ReportType
{
    public abstract string GetTitle();
}

public class PerformanceReport : ReportType
{
    public PerformanceReport()
    {
    }

    public override string GetTitle()
    {
        return "This is my Performance title";
    }
}

public class TrendingReport : ReportType
{
    public TrendingReport()
    {
    }

    public override string GetTitle()
    {
        return "This is my Trending title";
    }
}

public partial class Form1<T> : Form
    where T : ReportType, new()
{
    T foo = null;
    public Form1()
    {
        InitializeComponent();
        foo = new T();
        this.Text = foo.GetTitle();
    }
}

So, now I can do something like what was suggested:

Form1<TrendingReport> f = new Form1<TrendingReport>();

That's cool, and it works (my title depends on the type), but I don't think it helps my original issue. The 2nd enum.

If I am using a PerformanceReport, I want my GraphType enum to be:

public enum PerformanceGraph
{
    Bar,
    Line,
    Pie,
    Area
}

If I am using a TrendingReport, I want my GraphType enum to be:

public enum TrendingGraph
{
    Bar,
    Line
}

In other words, this enum is dependent upon the class (or as my original question stated, dependent upon the first enum).

I mean, I suppose I can put an enum, that encompasses all graph types, in the abstract class:

public abstract class ReportType
{
    public enum GraphType
    {
        Bar,
        Line,
        Pie,
        Area
    }

    public abstract string GetTitle();
}

But, that defeats the purpose of my original question. Because this now becomes legal (imagine that I modified the constructor of Form1):

Form1<TrendingReport> f = new Form1<TrendingReport>(ReportType.GraphType.Pie);

Remember, TrendingReport is supposed to only have Bar and Line. I'm trying to compile-time check this. I could definitely be missing something too (in regards to Generics).

A: 

Generic form?

  ReportInputForm<T>
Alex Reitbort
+7  A: 

If the type of the form depends on the values of the enums, I would turn the enums into types themselves. Then make the form class generic.

For example:

ReportInputForm<TrendingReport> = new ReportInputForm<TrendingReport>(GraphType.Area);

Or even:

ReportInputForm<AreaGraph> = new TrendingReportForm<AreaGraph>();

Assuming that TrendingReportForm is a subclass of ReportInputForm.

Anytime that you have a class with behavior that changes on the basis of some enumerated value, you should look for an opportunity to refactor out the changing behavior into a separate method that can be delegated to subclasses.

Daniel Pryden
I think it would be better to move the generic functionality to a separate class utilized by the form. Having the form itself be generic makes things tricky what with IDEs and designers.
Snarfblam
@Snarfblam: Presumably, though, the only form you're going to be editing with the visual designer is the base class. I might invert it, and make a generic class (not a subclass of `Form`) that is parameterized by the form class, like `TrendingReport<ReportInputForm>` -- depending on the application, this might even be the best solution.
Daniel Pryden
I may be a bit slow ... but I don't think I understand how to use Generics to solve my original problem. I'll edit my original question so that I can supply code.
JustLooking
+1 for helping me, thanks.
JustLooking
+1  A: 

This sounds like a perfectly reasonable place to use inheritance, with a base class ReportInputForm and child classes PerformanceReportInputForm, TrendingReportInputForm, and StatisticalReportInputForm. It doesn't seem particularly elaborate, and it neatly avoids any switch/case statements or long if chains while maintaining compile-time type checking. Alternatively, you could implement it in terms of the Strategy pattern, which adds some more scaffolding and overhead but allows you to change the report type dynamically.

qid
I went with something close to this. An abstract base class (ReportType), with four classes that inherit from it (basically these classes have a bunch of getters). I construct the inherited type I want outside of the form, then pass it to the form, and use that ReportType when I need something specific to the type (eliminating some if/else statements).
JustLooking