1

After investigating I concluded what string Enum with attributes is too slow and inconvenient for my purposes.

So my question - is my implementation normal or way redundant and dangerous?=)

public class StringEnum<TChildType> where TChildType: StringEnum<TChildType>, new()
{            
     private readonly Type childType;
     private string _value;
     private readonly HashSet<string> members;

     public string Value
     {
        get { return _value; }
        set
        {
            if (!Contains(value))
                {
                    throw new NotImplementedException(String.Format("Value '{0}' wasnt found in Enum", value));
                }
                _value = value;
            }
        }

        public IEnumerable<string> Values
        {
            get { return members; }
        }
        public bool Contains(string value)
        {
            return members.Contains(value);
        }

        public static IEnumerable<string> GetValues()
        {
            return Service.GetGenericConstProperties<string>(typeof(TChildType));
        }

        public StringEnum()
        {
            childType = typeof(TChildType);
            members = Service.GetGenericStaticProperties<string>(childType).ToHashSet();
            if (members.Count < 2) throw new Exception("Fill Enum!");               
        }

        public static implicit operator StringEnum<TChildType>(string str)
        {
            return new TChildType { Value = str };
        }

        public static implicit operator string(StringEnum<TChildType> source)
        {
            return source != null ? source.Value : null;
        }
    }

Enum Example:

public class PrinterType : StringEnum<PrinterType>
{
   public const string CommonPrinter = "... ...";
   .....
}

How to use:

public class MyPrinter
{
   public StringEnum<PrinterType> PrintType = PrinterType.CommonPrinter;
}

        var list = PrinterType.GetValues().ToList();
        var e = new MyPrinter();
        var q = e.PrintType;
        if (e.PrintType == PrinterType.Ярлыков)
           ...
        if (e.PrintType.Contains("jh"))

or even like backing Field:

        private StringEnum<PrinterType> _type;
        public string Type {... return _type... }
Roma Borodov
  • 596
  • 4
  • 10
  • I think this has been covered a lot before. e.g. http://stackoverflow.com/questions/424366/c-sharp-string-enums I'm not sure if you solution is exactly the same - the accepted answer there wasn't a generic class, but I think you are on the right lines if the enum-with-attributes isn't working for you. – Steve May 19 '15 at 08:38
  • 2
    Several down voters but no-one saying why, is there a problem with the question, it seems reasonable to me? – Steve May 19 '15 at 08:41
  • I know what there are a lot of same question) But still no one propose this kind of implementation! Maybe this post will help someone. This code seems powerful and perspective to me, and I interested a lot in objective criticism – Roma Borodov May 19 '15 at 08:46
  • I would say you should provide **numbers** in terms of performance. I mean, where's the benchmark where you can show us that `Enum` is slower than your own implementation of the same concept? – Matías Fidemraizer May 19 '15 at 08:53
  • 2
    If you want persons to look at your code that is working, normally http://codereview.stackexchange.com is a better place. This is a Question and Answer site. If you want to publish a piece of code, you can create a question that can be resolved by your piece of code, and then answer yourself with your piece of code... – xanatos May 19 '15 at 08:53
  • @MatíasFidemraizer I wouldn't call this an enum. An enum is something that historically has a name and a value. This has only a name. It is a closed-set `string`. – xanatos May 19 '15 at 08:54
  • @xanatos well, whatever. So then I can't understand why OP is comparing that "only name" with enums... – Matías Fidemraizer May 19 '15 at 08:55
  • @MatíasFidemraizer I didnt claim what Enum is a slow construction, but enum with string attributes cause more processor work, because of reflection. Here I have HashSet, so I avoided from f.e. calling getAtribute each time in case of attribute enums. Also imagine method getValues in case of enum with attributes. – Roma Borodov May 19 '15 at 09:20
  • @xanatos Thanks, didnt notice codereview.stackexchange.com – Roma Borodov May 19 '15 at 09:32

1 Answers1

1

I would change it this way:

public class StringEnum<TChildType> where TChildType : StringEnum<TChildType>, new()
{
    private static readonly HashSet<string> members;
    private static readonly List<string> sortedmembers;

    private string _value;

    public string Value
    {
        get { return _value; }

        protected set
        {
            if (!Contains(value))
            {
                throw new ArgumentException(String.Format("Value '{0}' wasnt found in Enum", value));
            }
            _value = value;
        }
    }

    public static IEnumerable<string> Values
    {
        get { return sortedmembers; }
    }

    public static bool Contains(string value)
    {
        return members.Contains(value);
    }

    static StringEnum()
    {
        sortedmembers = Service.GetGenericConstProperties<string>(typeof(TChildType)); // .ToList() if necessary
        members = new HashSet<string>(sortedmembers);

        if (members.Count < 2) throw new Exception("Fill Enum!");
    }

    public static implicit operator StringEnum<TChildType>(string str)
    {
        return new TChildType { Value = str };
    }

    public static implicit operator string(StringEnum<TChildType> source)
    {
        return source != null ? source.Value : null;
    }

    public static StringEnum<TChildType> Parse(string value)
    {
        return (StringEnum<TChildType>)value;
    }

    public override string ToString()
    {
        return (string)this;
    }

    public override int GetHashCode()
    {
        return StringComparer.Ordinal.GetHashCode(Value);
    }

    public override bool Equals(object obj)
    {
        StringEnum<TChildType> value = obj as StringEnum<TChildType>;

        if (object.ReferenceEquals(value, null))
        {
            return false;
        }

        return StringComparer.Ordinal.Equals(Value, value.Value);
    }
}

An enum, like all value types, is immutable. You can't change it. You can only reassign it. I'm doing the same here. The Value.set is protected, and can be used only internally/by the subclasser of StringEnum<>.

I've implemented the GetHashCode/Equals/ToString.

I've moved all the internal collections to static members, because they are common for all the StringEnum of the same TChildType

xanatos
  • 109,618
  • 12
  • 197
  • 280
  • Yeah, you absolutely right! I missed this static "stuff", it makes this code way better! Thanks a lot=) – Roma Borodov May 19 '15 at 09:26
  • One thing, maybe instead of static list, use dictionary or sortedset (never tried, not sure)? – Roma Borodov May 19 '15 at 09:30
  • 1
    @RomaBorodov No, the `List<>` is to have the elements sorted **as they are received** from the `Service`. For quick access there is the `HashSet<>` – xanatos May 19 '15 at 09:37