Связывание перечислений со строкой c# - PullRequest
0 голосов
/ 27 мая 2020

После этого сообщения: Связывание перечислений со строками в C#

Я хотел go дальше, поскольку он не полностью отвечал моим потребностям в Enum, подобном Class, который будет действовать как строка. В итоге я получил решение, которое позволяет мне делать следующее:

   string test1 = TestEnum.Analyze;  //test1 == "ANALYZE"
   string test1bis = (string)TestEnum.Analyze; //test1bis == "ANALYZE"
   TestEnum test2 = "ANALYZE";  //test2 == {ANALYZE}
   TestEnum test3 = "ANYTHING"; //test3 == null

Как показано ниже в unitTests, все они отлично работают с этим:

 public class TestEnum : EnumType<TestEnum> 
    {
        public static TestEnum Analyze { get { return new EnumType<TestEnum>("ANALYZE"); } }
        public static TestEnum Test { get { return new EnumType<TestEnum>("TEST"); } }

        public static implicit operator TestEnum(string s) => (EnumType<TestEnum>) s;
        public static implicit operator string(TestEnum e) => e.Value;
    }

Я не могу решить, элегантно это решение или невероятно глупо. Мне кажется, что это, вероятно, ненужное сложное, и я могу испортить гораздо более простое решение, в любом случае оно может кому-то помочь, поэтому я помещаю его здесь.

 //for newtonsoft serialization
    [JsonConverter(typeof(EnumTypeConverter))]
    public  class EnumType<T>  where T : EnumType<T> , new()
    {
        public EnumType(string value= null)
        {
            Value = value;
        }

        //for servicestack serialization
        static  EnumType()
        {

                JsConfig<EnumType<T>>.DeSerializeFn = str =>
                {
                    return (T)str ;
                };
                JsConfig<EnumType<T>>.SerializeFn = type =>
                {
                    return type.Value;
                };

                JsConfig<T>.DeSerializeFn = str =>
                {
                    return (T)str;
                };
                JsConfig<T>.SerializeFn = type =>
                {
                    return type.Value;
                };


        }
        protected string Value { get; set; }


        public static T Parse(string s)
        {
            return (T)s;

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

        public static EnumType<T> ParseJson(string json)
        {
            return (T)json;
        }


        public static implicit operator EnumType<T>(string s)
        {
            if (All.Any(dt => dt.Value == s))
            {
                return new T { Value = s };
            }
            else
            {
                var ai = new Microsoft.ApplicationInsights.TelemetryClient(Connector.tconfiguration);
                ai.TrackException(new Exception($"Value {s} is not acceptable value for {MethodBase.GetCurrentMethod().DeclaringType}, Acceptables values are {All.Select(item => item.Value).Aggregate((x, y) => $"{x},{y}")}"));
                return null;
            }
        }

        public static implicit operator string(EnumType<T> dt)
        {
            return dt?.Value;
        }

        public static implicit operator EnumType<T>(T dt)
        {
            if (dt == null) return null;
            return new EnumType<T>(dt.Value);
        }


        public static implicit operator T(EnumType<T> dt)
        {
            if (dt == null) return null;
            return new T { Value = dt.Value };
        }

        public static bool operator ==(EnumType<T> ct1, EnumType<T> ct2)
        {
            return (string)ct1 == (string)ct2;
        }

        public static bool operator !=(EnumType<T> ct1, EnumType<T> ct2)
        {
            return !(ct1 == ct2);
        }


        public override bool Equals(object obj)
        {
            try
            {
                if(obj.GetType() == typeof(string))
                {
                    return Value == (string)obj;
                }

                return Value == obj as T;
            }
            catch(Exception ex)
            {
                return false;
            }
        }



        public override int GetHashCode()
        {
            return (!string.IsNullOrWhiteSpace(Value) ? Value.GetHashCode() : 0);
        }

        public static IEnumerable<T> All
         => typeof(T).GetProperties()
           .Where(p => p.PropertyType == typeof(T))
           .Select(x => (T)x.GetValue(null, null));


        //for serialisation
        protected EnumType(SerializationInfo info,StreamingContext context)
        {
            Value = (string)info.GetValue("Value", typeof(string));
        }
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("Value",Value);

        }


    }

Вот модульные тесты:

 [TestFixture]
    public class UnitTestEnum
    {
        Connector cnx { get;set; }
        private class Test
        {
            public TestEnum PropertyTest { get; set; }
            public string PropertyString { get; set; }
        }

        [SetUp]
        public void SetUp()
        {
            typeof(EnumType<>)
               .Assembly
               .GetTypes()
               .Where(x => x.BaseType?.IsGenericType == true && x.BaseType.GetGenericTypeDefinition() == typeof(EnumType<>))
               .Each(x =>
                System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(x.BaseType.TypeHandle)

               );
            cnx = new Connector(); 
        }
        [TearDown]
        public void Clear()
        {
            cnx.Dispose();
        }
        [Test]
        public void EqualsString()
        {
            Assert.AreEqual(TestEnum.Analyze, TestEnum.Analyze);
            Assert.AreEqual(TestEnum.Analyze,"ANALYZE");
            Assert.IsTrue("ANALYZE" == TestEnum.Analyze); 

            Assert.IsTrue("ANALYZE".Equals(TestEnum.Analyze));
        }

        [Test]
        public void Casts()
        {

            string test1 = TestEnum.Analyze;
            string test1bis = (string)TestEnum.Analyze;
            TestEnum test2 = "ANALYZE";
            TestEnum test3 = "NAWAK";

            Assert.AreEqual("ANALYZE", test1);
            Assert.AreEqual("ANALYZE", test1bis);
            Assert.IsTrue(test2 == TestEnum.Analyze);
            Assert.IsTrue(test2.Equals(TestEnum.Analyze));
            Assert.AreEqual(test3, null);

        }

        [Test]
        public void Deserializations()
        {

            new List<TestEnum>
            {
                (TestEnum)ServiceStack.Text.JsonSerializer.DeserializeFromString("\"ANALYZE\"", typeof(TestEnum)),
                "\"ANALYZE\"".FromJson<TestEnum>(),
                (TestEnum)Newtonsoft.Json.JsonConvert.DeserializeObject("\"ANALYZE\"", typeof(TestEnum)),
                Newtonsoft.Json.JsonConvert.DeserializeObject<TestEnum>("\"ANALYZE\"")
            }.Each(testEnum => Assert.AreEqual(testEnum, TestEnum.Analyze));




            new List<Test>
            {
                "{\"PropertyTest\":\"ANALYZE\",\"PropertyString\":\"ANALYZE\"}".FromJson<Test>(),
                (Test)ServiceStack.Text.JsonSerializer.DeserializeFromString("{\"PropertyTest\":\"ANALYZE\",\"PropertyString\":\"ANALYZE\"}", typeof(Test)),
                Newtonsoft.Json.JsonConvert.DeserializeObject<Test>("{\"PropertyTest\":\"ANALYZE\",\"PropertyString\":\"ANALYZE\"}"),
                (Test)Newtonsoft.Json.JsonConvert.DeserializeObject("{\"PropertyTest\":\"ANALYZE\",\"PropertyString\":\"ANALYZE\"}",typeof(Test))
            }.Each(test =>
            {
                Assert.AreEqual(test.PropertyTest, TestEnum.Analyze);
                Assert.AreEqual(test.PropertyString, "ANALYZE");
            });





        }

        [Test]
        public void Serialisations()
        {



            Assert.AreEqual("{\"PropertyTest\":\"ANALYZE\",\"PropertyString\":\"ANALYZE\"}", new Test { PropertyTest = TestEnum.Analyze, PropertyString = TestEnum.Analyze }.ToJson());
            Assert.AreEqual("{\"PropertyTest\":\"ANALYZE\",\"PropertyString\":\"ANALYZE\"}", Newtonsoft.Json.JsonConvert.SerializeObject(new Test { PropertyTest = TestEnum.Analyze, PropertyString = TestEnum.Analyze }));
            Assert.AreEqual("\"ANALYZE\"", TestEnum.Analyze.ToJson());
            Assert.AreEqual("\"ANALYZE\"", Newtonsoft.Json.JsonConvert.SerializeObject(TestEnum.Analyze));
        }


        [Test]
        public void TestEnums()
        {

            Assert.AreEqual(TestEnum.All.Count(), 2);
            Assert.Contains(TestEnum.Analyze,TestEnum.All.ToList());
            Assert.Contains(TestEnum.Test,TestEnum.All.ToList());

        }

1 Ответ

2 голосов
/ 27 мая 2020

Элегантный код обычно описывается как простой, чистый, краткий, с ясным намерением и оптимально производительный, я не вижу здесь ни одной из этих черт, что особенно запутано, поскольку вместо использования простых C# Enum, как предполагалось, они обернуты в общем c базовом классе с неявными приведениями и настраиваемой обработкой сериализации для разных JSON библиотек сериализации (что вряд ли будет работать в других библиотеках / форматах сериализации) и со всей добавленной дополнительной искусственной сложностью даже не ясно, в чем преимущество или цель всего шаблона?

Код, который утонул в таком большом количестве шаблонов, увеличивает нагрузку на обслуживание и препятствует рефакторингу, поскольку никто другой не узнает, каковы желаемые намерения или цель кода & почему вместо этого не были приняты более простые наивные решения.

Если вы просто хотите иметь другой формат сериализации проводов, отличный от имени символа, используемого в коде (который IMO должен иметь вескую причину, чтобы отличаться ferent), вы можете просто использовать атрибут [EnumMember] Serialization:

[DataContract]
public enum TestEnum 
{
    [EnumMember(Value = "ANALYZE")]
    Analyze,
    [EnumMember(Value = "TEST")]
    Test,
}

В противном случае я бы отказался от использования Enums и go обратно к использованию строковых констант, которые проще, быстрее, эффективнее с точки зрения памяти и работает во всех библиотеках сериализации без специальных хаков сериализации:

public static class MyConstants
{
    public const string Analyze = "ANALYZE";
    public const string Test = "TEST";
}
...