开发者

C# enum that depends upon another enum [or maybe this is more design related]

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 : R开发者_运维知识库eportType, 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).


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.


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.


Generic form?

  ReportInputForm<T>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜