在C#中,在方法内定义枚举是什么意思?

31

我的主要背景是C++,现在正在学习C#。因此,我需要一些关于C#惯用语和风格的帮助。

我试图用C#编写一个小型文本文件解析方法,在其中需要一个具有三种状态的简单状态变量。在C++中,我会像这样声明一个enum来定义状态变量:

enum { stHeader, stBody, stFooter} state = stBody;

...然后像这样在我的解析循环中使用它:

if (state == stHeader && input == ".endheader")
{
  state = stBody;
}
在C#中,我意识到不可能在方法内声明enum。那么,为了代码风格怎么办呢?在方法外部声明这个内部enum?使用魔法数字1、2、3?还是创建一个单独的类来处理它?请帮助我澄清我的疑惑。

也许我错过了什么,但如果枚举仅在单个方法中使用,那有什么意义呢? - Doggett
6
@Dogget - 给州起易于阅读的名称。否则我将不得不编写 if (state == 2 && input == ".endheader") state = 1; 的语句。 - danatel
2
自我注释状态等在C语言中非常常见,例如在给定的示例中,状态通常是比int更有意义的东西。我必须说,在C#中这种情况出现得较少 - 主要是因为您倾向于将方法分解为较小的组件、对象等,而不是像C那样更加命令式。或者说,当它类似于C时使用的C++也是如此。 - Mania
5个回答

37

您能做的最接近的方式是在类中使用私有嵌套枚举:

public class TheClass
{
    private enum TheEnum 
    {
       stHeader, 
       stBody, 
       stFooter
    }

    // ...the rest of the methods properties etc... 
}

14
这是变态的,因为状态在方法之外完全没有意义。这就像在方法之外声明循环变量一样。此外,你需要给它一个名字! - danatel
14
欢迎来到保守型企业语言的世界。虽然情况可能更糟,比如java :D - Matt Greer
1
@danatel:我不制定规则 :) - Willem van Rumpt
1
@willem van Rumpt。感谢您的回答。对我来说,声明三个独立的常量(const int stHeader=0;)似乎是较小的恶。 - danatel
1
@danatel:我个人会选择枚举,但本质上来说,它们归结为相同的事情 :) - Willem van Rumpt
不,这不是什么变态的东西……在CLR世界中,最小的封装单元是类型。CLR处理的是类型及其方法。 - Paul Michalik

10

你也可以使用常量变量,但我更喜欢并认为使用枚举更好的编码风格。

 public class Class1
{
    private enum TheEnum
    {
        stHeader,
        stBody,
        stFooter
    }
    public void SomeMethodEnum()
    {
        TheEnum state = TheEnum.stBody;
        switch (state)
        {
            case TheEnum.stHeader:
                //do something
                break;
            case TheEnum.stBody:
                break;
            case TheEnum.stFooter:
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }
    }


    public void SomeMethodConst()
    {
        int state = 1;
        const int Header = 1;
        const int Body = 2;
        const int Footer = 3;

        switch (state)
        {
            case Header:
                break;
            case Body:
                break;
            case Footer:
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }

    }
}

0

在方法作用域中无法声明enum(回顾上面Willem van Rumpt的答案),但是您可以声明一个内部私有类来进行状态跟踪,并在类方法中重复使用它。包含类或其客户端不需要知道该辅助类。

class Program
{
    private class Parser
    {
        public string pname = "ParserPam"; 
        public enum pstate { rewind=-1, stdHeader, stBody, stFooter }

        public pstate state = pstate.stdHeader; 

        //Implement state transition logic as methods
        public void tick() => this.state = pstate.stBody;
        public void tock() => this.state = pstate.stFooter;
        public void rewind() => this.state = this.state - 1;

        public override string ToString()
        {
            return $"{this.pname} state: {this.state}";
        }
    }

    static void ParseFile(string filename)
    { 
        Parser fp = new Parser(); //This object tracks your method's state

        Console.WriteLine(fp); // "ParserPam state: stdHeader"
        if (fp.state == Parser.pstate.stdHeader)
        { 
            fp.tick(); // Transition
            // Do stuff
        }

        // Do more stuff
        Console.WriteLine(fp); // "ParserPam state: stBody"
        fp.tock();
        Console.WriteLine(fp); // "ParserPam state: stFooter"
        fp.rewind();
        Console.WriteLine(fp); // "ParserPam state: stBody"
    }

    static void Main(string[] args)
    {
        ParseFile( @"C:\Example" ); // Client call
    }
}

在这种情况下,Parser 是您的私有内部类,其实例可以跟踪状态 - 它在Program之外不可见。 ParseFile 是一个利用Parser的方法。

输出

ParserPam state: stdHeader
ParserPam state: stdBody
ParserPam state: stFooter
ParserPam state: stBody

0
我也倾向于使用枚举的方法。此外,虽然这里不是这种情况,但当您想将它们组合为标志时,它比CONSTS具有优势,因为您有一种轻松测试的方法。

0

我知道这已经过时了,所以这主要是为那些寻找想法的人而准备的。

在方法内部创建一个“枚举”类型、干净结构所需的最接近的方法,我能想到的就是使用字典。

var stateOpt = new Dictionary<string, byte>() { { "stHeader", 0 }, { "stBody", 1 }, { "stFooter", 2 } };
var state = stateOpt["stBody"];

然后您可以在条件中执行所需的操作...

if (state == stateOpt["stHeader"] && input == ".endheader")
{
  state = stateOpt["stBody"];
}

OP并不是在寻求最有效或最具成本效益的解决方案,他们只是为了“代码风格的整洁”而提问,我认为这很好也很干净。 - samhaz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接