How to create methods in an enum?

8

I have a Java application and I'm porting it to C #. I have a question regarding enum that seems to work differently than Java. After you have asked a question here on the site ( Enumerations can contain abstract methods? ), whenever possible opt to centralize methods, and properties in enum . Well, in Java my code looks like this:

enum BBcodes {

   BOLD("b"),
   ITALIC("i"),
   //...
   UNDERLINE("u");

   private final String code;
   public BBcodes(String code){
      this.code = code;
   }

   public string wrap(String contents){
      return MessageFormat.format("[{0}]{1}[/{0}]", this.code, contents);
   }
}

In which I can call it like this:

String bbcode = BBcodes.BOLD.wrap("StackOverflow"); //[b]StackOverflow[/b]

I saw on this question that I can not create a enum that stores values of type string . I do not know if this is the best way to solve it, but I created a class for it:

public class BBcodes
{
   public static readonly string BOLD = "b";
   public static readonly string ITALIC = "i";
   //...

   public static string Wrap(string code, string contents)
   {
      return string.Format("[{0}]{1}[/{0}]", code, contents);
    }
}

In what I call it:

string bbcode = BBcodes.Wrap(BBcodes.BOLD, "StackOverflow"); //[b]StackOverflow[/b]

It is not a problem to do this, having to pass its BBCode value as argument to the Wrap method. It's something to get used to. :)

But if possible, I would like to do something as close to Java as possible, creating everything (the values and methods) in enum itself. How could I do this in C #? It's possible?

    
asked by anonymous 17.04.2016 / 06:54

2 answers

8

In C # enumeration types are treated differently than other types. Not a class as it is in Java . In fact you have found one of the few things that clearly C # is worse than Java and it is not possible to place methods within the enumerations. It is good to point out that a C # enumeration is a type by value and is usually equivalent to an integer (it can specify the type) and has up to another problem that is the cast implicit of these types, but this is another subject.

The closest solution that is used and some even think it might be more elegant in some scenarios is the use of extension methods ( see that it has its advantages and disadvantages ). In Jon Skeet's answer link above in the question from the original author here has the similar solution:

public enum BBCodes {
    [Description("b")]
    Bold = 1,
    [Description("i")]
    Italic = 2
}

public static class BBCodesExt {
    public static string Wrap(this BBCodes code, string contents) {
        return $"[{code.ToStringDescription()}]{contents}[/{code.ToStringDescription()}]";
    }
    public static string ToStringDescription(this BBCodes code) {
        var attributes = (DescriptionAttribute[])code.GetType().GetField(code.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
        return attributes.Length > 0 ? attributes[0].Description : "";
    }
}

I've improved the C # style .

Call it like this:

string bbcode = BBCodes.Bold.Wrap("StackOverflow");

See working on dotNetFiddle .

Documentation .

But if the string representation hits with the member name in all cases, as was shown in the 2 members of the example, neither does it need this. enum has a method ToString() for this.

But if what you want is the reference semantics of the class (I doubt you'll need it in most cases), then you'd better simulate the enumeration, more or less as you did. Just try to make it more idiomatic for C # as shown and making the class be sealed (equivalent to final ). Or you can do it a little differently:

public sealed class BBCodes {
    public static readonly string Bold = "b";
    public static readonly string Italic = "i";
    //...

    private string code;

    public BBCodes(string code) {
        this.code = code;
    }

    public string Wrap(string contents)
    {
        return $"[{code}]{contents}[/{code}]";
    }
}

You can create a property to give read / write access to the instance member value, if necessary.

See working on dotNetFiddle .

    
17.04.2016 / 08:34
6

Enumerations ( enum ) in C # accept some types ( byte , sbyte , short , ushort , int , uint , long ulong is not between them. Also it is not possible to create methods within an enumeration, but still it is possible to arrive at the desired result without much suffering.

The output is the creation of an extension method for the desired enumeration type. Next I'll build a solution based on the code of your question.

Create the enumeration:

public enum BBCode
{
    [Description("b")]
    Bold,

    [Description("i")]
    Italic,

    [Description("u")]
    Underline
}

In the above enumeration statement, I decorated the constants with the string whose purpose is to assign a description in DescriptionAttribute to any element of an application.

Create an extension method for the type of enumeration you want:

public static class MyEnumExtensions
{
    public static string Wrap(this BBCode val, string content)
    {
        var attributes = (DescriptionAttribute[])val.GetType().GetField(val.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);

        if (attributes.Length <= 0)
            throw new Exception($"Not decorated with '{nameof(DescriptionAttribute)}'.");

        var code = attributes[0].Description;
        return $"[{code}]{content}[/{code}]";
    }
}

The string extension method, defined above, will only be accessible for enumerations of type Wrap (hence BBCode ) and its function is to get the value contained in the this BBCode attribute, which was used to decorate a referenced constant, and return a DescriptionAttribute formatted together with the value of the string parameter.

To use this extension method, simply call it over an instance of the content enumeration.

BBCode.Bold.Wrap("Foo"); // Retorna: "[b]Foo[/b]"

Note that the BBCode class must be in an accessible namespace so that the MyEnumExtensions method can be called.

In this way you have a behavior very similar to the original Java code presented in the question.

Another solution would be to use a class that would behave as an enumeration, as presented in your own question in your attempt to implement in C #. But anyway you are free to choose the way that suits you best for each scenario, as both have their advantages and disadvantages.

    
17.04.2016 / 08:33