C# 枚举依赖于另一个枚举 [或者这可能与设计更相关]

发布于 2024-08-10 06:16:12 字数 4597 浏览 2 评论 0原文

我可能会走错方向,所以让我尝试理清我的想法(并希望从你们那里得到一些提示):

想象一个枚举:

public enum ReportType
{
   Performance,
   Trending,
   Statistical
}

这个枚举也将是表单构造函数的属性或参数。 ReportType 的值将确定以下内容:

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

对于第二个枚举,如果它是 Performance ReportType,我会想要:

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

如果它是 Trending,我会想要:

public enum TrendingGraph
{
    Bar,
    Line
}

该表单只是收集用户输入。我的意思是,我不想为了简单的形式而陷入一些复杂的继承结构。当我可以快速执行以下操作时,似乎需要付出很大的努力(我可能是错的):(

想象一下这个构造函数)

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");
    }
}

当您想实际使用 GraphCombo 执行某些操作时,它开始变得毛茸茸的,因为现在您需要知道它是哪个 ReportType用于铸造目的。

另外,回到我的构造函数,假设我想向它发送一个图形值,以便我们可以设置 GraphCombo 的 SelectedIndex (这可能是用户最后选择的值,而不是设置某种类型的默认值)。嗯...

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

我的意思是,现在有三个图形枚举,因为有三个 ReportType。另外,想象一下添加更多的 ReportType。

我想我可以使用 int/long:

 public ReportInputForm(ReportType RptType, int lastGraphType)
 {
 }

但我想我想通过编译时检查成为 Enum 先生。换句话说,如果我可以使用某种类型的枚举(像这样),我就可以检测到错误:

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

而不是:

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

我已经闲聊得够多了。也许这更多是一个设计问题而不是与枚举相关。我只是在寻找一些关于如何解决这个问题的想法。谢谢!

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

这是对使用泛型的回应(如果我有点慢,我很抱歉;谢谢丹尼尔的回复):

这是一个建议:

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

好的,所以如果我要使用泛型(和表单),我有创建一个类型。我正在想象这样的事情(我们现在只公开一个函数,设置标题):

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();
    }
}

所以,现在我可以做一些类似于建议的事情:

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

这很酷,而且它有效(我的标题取决于类型) ,但我认为这对我原来的问题没有帮助。第二个枚举。

如果我使用 PerformanceReport,我希望我的 GraphType 枚举为:

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

如果我使用 TrendingReport,我希望我的 GraphType 枚举为:

public enum TrendingGraph
{
    Bar,
    Line
}

换句话说,该枚举取决于类(或者正如我原来的问题所述,取决于在第一个枚举上)。

我的意思是,我想我可以在抽象类中放置一个包含所有图形类型的枚举:

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

    public abstract string GetTitle();
}

但是,这违背了我最初问题的目的。因为现在这变得合法了(想象一下我修改了 Form1 的构造函数):

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

记住,TrendingReport 应该只有 Bar 和 Line。我正在尝试编译时检查这一点。我也肯定会错过一些东西(关于泛型)。

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

江湖正好 2024-08-17 06:16:12

如果表单的类型取决于枚举的值,我会将枚举本身转换为类型。然后使表单类通用。

例如:

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

甚至:

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

假设 TrendingReportFormReportInputForm 的子类。

每当您的类的行为根据某些枚举值而变化时,您都应该寻找机会将变化的行为重构为可以委托给子类的单独方法。

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.

北渚 2024-08-17 06:16:12

这听起来是一个使用继承的完全合理的地方,具有基类 ReportInputForm 和子类 PerformanceReportInputFormTrendingReportInputFormStatisticalReportInputForm< /代码>。它看起来并不特别复杂,并且它巧妙地避免了任何 switch/case 语句或长 if 链,同时保持编译时类型检查。或者,您可以根据策略模式来实现它,这会增加一些脚手架和开销,但允许您动态更改报告类型。

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.

罪歌 2024-08-17 06:16:12

通用形式?

  ReportInputForm<T>

Generic form?

  ReportInputForm<T>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文