首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C# -在.Net内核中重新应用JS对象的操作

C# -在.Net内核中重新应用JS对象的操作
EN

Code Review用户
提问于 2020-03-07 18:37:19
回答 2查看 415关注 0票数 2

重要:

我用一些改进做了另一个版本:第2版

几个月前,我不同意在dynamic .Net Core中使用本机/代码方法来操作C#或.Net,因此在ExpandoObject中动态地操作、读取和编写道具是非常麻烦的。

所以,在网上搜索,我找不到一个能满足我需求的解决方案。我正在寻找你的意见和反馈,以评价以下代码和结果。

帮我理解一下,如果我想发明热水的话?或者如果在核心中存在类似的东西。

...if不是,而且我采用了正确的方法,我想听听关于如何改进我的代码的建议,仅仅是因为我将在REST服务中实现,而使这个类更快的任何额外的调整都会更好。

因此,完整的功能示例可以找到它这里

C#代码:

代码语言:javascript
复制
namespace FW {
    public class Expando
    {
        public Expando(dynamic value)
        {
            expando = ToExpando(value);
        }

        public ExpandoObject root { get => expando; }

        private ExpandoObject expando { get; set; }

        private ExpandoObject ToExpando(dynamic dynamicObject)
        {
            if ((dynamicObject as object).GetType().Name == "ExpandoObject") return dynamicObject;

            if (!(dynamicObject as object).GetType().IsGenericType) throw new Exception("No generic type");

            ExpandoObject expando = new ExpandoObject();

            ((object)dynamicObject)
            .GetType()
                .GetProperties()
                .ToList()
                .ForEach(p => expando.fwAddProperty(p.Name, p.GetValue(dynamicObject) as object));

            return expando;
        }

        public dynamic this[string prop]
        {
            get => expando.fwReadProperty(prop);
            set => expando.fwAddProperty(prop, value as object);
        }

        public dynamic this[params string[] props]
        {
            get
            {
                ExpandoObject returnValue = expando;

                foreach (string prop in props)
                {
                    var temp = returnValue.fwReadProperty(prop);

                    try { returnValue = ToExpando(temp); }
                    catch { return temp as object; }
                }

                return returnValue;
            }
            set
            {
                List list = new List();
                list.Add(expando);

                foreach (var prop in props)
                {
                    var newProp = list.Last().fwReadProperty(prop);

                    if (newProp != null)
                    {
                        try { list.Add(ToExpando(newProp)); }
                        catch { }
                    }
                    else if (prop != props.Last())
                    {
                        ExpandoObject expandoTemp = new ExpandoObject();
                        list.Add(expandoTemp);
                    }
                }

                List nodeProps = props.ToList();
                list.Last().fwAddProperty(nodeProps.Last(), value as object);

                nodeProps.RemoveAt(nodeProps.Count - 1);

                ExpandoObject ExpandoTemp = list.Last();
                list.RemoveAt(list.Count - 1);

                while (list.Count != 0)
                {
                    var node = list.Last();
                    list.RemoveAt(list.Count - 1);

                    node.fwAddProperty(nodeProps.Last(), ExpandoTemp as object);
                    nodeProps.RemoveAt(nodeProps.Count - 1);

                    ExpandoTemp = node;
                }

                expando = ExpandoTemp;
            }
        }
    }

    public static class extExpandoObject
    {
        public static void fwAddProperty(this ExpandoObject expando, string propertyName, object propertyValue)
        {
            // ExpandoObject supports IDictionary so we can extend it like this
            var expandoDict = expando as IDictionary;

            if (expandoDict.ContainsKey(propertyName))
                expandoDict[propertyName] = propertyValue;
            else
                expandoDict.Add(propertyName, propertyValue);
        }

        public static object fwReadProperty(this ExpandoObject expando, string propertyName)
        {
            // ExpandoObject supports IDictionary so we can extend it like this
            var expandoDict = expando as IDictionary;

            if (expandoDict.ContainsKey(propertyName))
                return expandoDict[propertyName];
            else
                return null;
        }
    }
}

实现

代码语言:javascript
复制
public class Program
{
    public static void Main()
    {
        FW.Expando dynamicObject = 
            new FW.Expando(new
                           {
                               a = new int[] { 1, 2 },
                               b = "String val",
                               c = 10,
                               d = new { sa = 1, sb = "abv", sc = new int[] { 1, 2, 3 } }
                           });

        // Add new props
        const string newProp = "e";
        dynamicObject[newProp] = "New val";
        dynamicObject["f"] = false;

        dynamicObject["d", "sd"] = "SDSDSD";
        var a = dynamicObject["d", "sd"];

        dynamicObject["d", "se"] = null;

        // Modify props
        const string prop = "a";
        dynamicObject[prop] = (dynamicObject[prop] as int[]).Append(3).ToArray();
        dynamicObject["b"] += " ABCD";

        // Modify children props of another prop
        dynamicObject["d", "sb"] = new string[] { "New", "Array" };

        dynamicObject["d", "sa"] += 5;

        dynamicObject["d", "sa"] = new { dz = "ABA", zz = "WCC", ZXXX = new { Y1 = "1", Y2 = "2" } };

        dynamicObject["parent", "node"] = "New field";

        dynamicObject["parent-node", "node-lvl1", "node-lvl1.1"] = "P > 1 > 1.1";
        dynamicObject["parent-node", "node-lvl1", "node-lvl1.2"] = "P > 1 > 1.2";
        dynamicObject["parent-node", "node-lvl2", "node-lvl2.1"] = "P > 2 > 2.1";
        dynamicObject["parent-node", "m-node", "sub1", "sub2", "sub3"] = "3 Sublevels";

        // Read props
        object propValue = dynamicObject[prop];
        object propValueString = dynamicObject["b"];

        string result = Newtonsoft.Json.JsonConvert.SerializeObject(dynamicObject.root);

        // CHECK MORE EASILY THE RESULT: https://jsonformatter.curiousconcept.com/
        Console.WriteLine("\r\n" + result + "\r\n");
    }
}

JSON结果:

代码语言:javascript
复制
{
   "a":[
      1,
      2,
      3
   ],
   "b":"String val ABCD",
   "c":10,
   "d":{
      "sa":{
         "dz":"ABA",
         "zz":"WCC",
         "ZXXX":{
            "Y1":"1",
            "Y2":"2"
         }
      },
      "sb":[
         "New",
         "Array"
      ],
      "sc":[
         1,
         2,
         3
      ],
      "sd":"SDSDSD",
      "se":null
   },
   "e":"New val",
   "f":false,
   "parent":{
      "node":"New field"
   },
   "parent-node":{
      "node-lvl1":{
         "node-lvl1.1":"P > 1 > 1.1",
         "node-lvl1.2":"P > 1 > 1.2"
      },
      "node-lvl2":{
         "node-lvl2.1":"P > 2 > 2.1"
      },
      "m-node":{
         "sub1":{
            "sub2":{
               "sub3":"3 Sublevels"
            }
         }
      }
   }
}

谢谢你抽出时间阅读。

EN

回答 2

Code Review用户

回答已采纳

发布于 2020-03-13 23:30:24

如果您想要一个更干净的dynamic/ExpandoObject版本,我会一步一步地修复您不喜欢的内容。假设您想做一些类似Javascript的语法:

代码语言:javascript
复制
obj = {};
obj.a = 123;
obj.b = "message";
obj.c = [ 456, "something", {} ];
obj.d = {
    x: 50,
    y: [ "a", "b", "c" ],
    z: null
};

obj.e = "eeee";
obj.d.z = { z: "zz" };
obj.d.y.Add("d");

使用开箱即用的动态/ExpandoObject将其转换为等效的语法:

代码语言:javascript
复制
dynamic obj = new ExpandoObject();
obj.a = 123;
obj.b = "message";
obj.c = new List { 456, "something", new ExpandoObject() };
obj.d = new ExpandoObject();
obj.d.x = 50;
obj.d.y = new List { "a", "b", "c" };
obj.d.z = null;

obj.e = "eeee";
obj.d.z = new ExpandoObject();
obj.d.z.z = "zz";
obj.d.y.Add("d");

语法的第一个主要问题是没有快捷语法用于创建ExpandoObject并将其属性设置为普通对象:

代码语言:javascript
复制
// Can't do this...
obj.d = new ExpandoObject()
{ 
    x = 50,
    y = new List { "a", "b", "c" },
    z = null
}

一种尝试是创建并转换一个匿名对象(就像您在代码中所做的那样),但是这需要缓慢的反射,并且会带来其他挑战。相反,我认为最干净的解决方案应该是在返回ExpandoObject之前创建一个用于创建一个并以某种方式初始化它的助手:

代码语言:javascript
复制
public static class Dynamic
{
    public static dynamic Object(Action init)
    {
        var obj = new ExpandoObject();
        init(obj);
        return obj;
    }
}

这使得内联对象更好:

代码语言:javascript
复制
dynamic obj = new ExpandoObject();
obj.a = 123;
obj.b = "message";
obj.c = new List { 456, "something", new ExpandoObject() };
obj.d = Dynamic.Object(o =>
{
    o.x = 50;
    o.y = new List { "a", "b", "c" };
    o.z = null;
});

obj.e = "eeee";
obj.d.z = Dynamic.Object(o => o.z = "zz");
obj.d.y.Add("d");

从这里开始,语法非常接近,我只想添加一些东西来使命名/样式更加一致:

代码语言:javascript
复制
public static class Dynamic
{
    public static dynamic Object(Action init)
    {
        var obj = new ExpandoObject();
        init(obj);
        return obj;
    }

    public static dynamic Object() => Object(_ => {});

    public static List List(params dynamic[] items) => items.ToList();
}

用法:

代码语言:javascript
复制
dynamic obj = Dynamic.Object();
obj.a = 123;
obj.b = "message";
obj.c = Dynamic.List(456, "something", Dynamic.Object());
obj.d = Dynamic.Object(o =>
{
    o.x = 50;
    o.y = Dynamic.List("a", "b", "c");
    o.z = null;
});

obj.e = "eeee";
obj.d.z = Dynamic.Object(o => o.z = "zz");
obj.d.y.Add("d");

Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(obj,
    Newtonsoft.Json.Formatting.Indented));

输出:

代码语言:javascript
复制
{
  "a": 123,
  "b": "message",
  "c": [
    456,
    "something",
    {}
  ],
  "d": {
    "x": 50,
    "y": [
      "a",
      "b",
      "c",
      "d"
    ],
    "z": {
      "z": "zz"
    }
  },
  "e": "eeee"
}

您还可以使用其他策略来实现Dynamic.Object和其他助手,但它们都取决于您自己的个人风格/偏好。如果缺少一些关键功能,请告诉我,我很乐意再添加几个助手。

票数 3
EN

Code Review用户

发布于 2020-03-08 16:21:27

我喜欢这个代码的简单性。它可能需要一些改进,但这些只是为了让它更灵活地被其他项目采用(如果它是为了这个目的的话)。

在大多数情况下,您已经将主要的功能放在扩展类上,这是我自己不会做的事情。我认为最好在主类中添加、删除、获取和设置功能。然后,如果你想给他们打个分机,就给他们回电话。这将使它们更易于维护,并且易于在未来的项目中扩展。

此外,代码缺少null验证,您必须始终考虑这些验证。

我注意到的另一件事是,有许多boxingunboxing (动态对象,反之亦然)。尽量减少这些铸件。

最后,我建议将类调整为ExpandoObject之上的一个瘦层,这意味着简化它的功能,并使用接口来订立一个契约,在将来的使用中有所帮助。

以下是我心中的想法,也许会有一些有用的东西(我希望):

代码语言:javascript
复制
public interface IExpando
{
    void AddOrUpdateProperty(string propertyName, object propertyValue);

    dynamic GetProperty(string propertyName);

    bool RemoveProperty(string propertyName);

    IDictionary GetProperties();
    // any other properties or methods that you think it's a must have
}

public class Expando : IExpando
{
    private readonly ExpandoObject _root;

    public Expando(dynamic value) { _root = InitiateInstance(value); }

    public dynamic this[string propertyName] 
    {
        get => GetProperty(propertyName);
        set => AddOrUpdateProperty(propertyName, value);
    }

    private ExpandoObject InitiateInstance(dynamic value)
    {
        if (value is null) { throw new ArgumentNullException(nameof(value)); }

        if (value.GetType() == typeof(ExpandoObject)) { return value; }

        if (!value.GetType().IsGenericType) { throw new Exception("No generic type"); }

        _root = new ExpandoObject();

        (value as object)?
        .GetType()
            .GetProperties()
            .ToList()
            .ForEach(p => AddOrUpdateProperty(p.Name, p.GetValue(value)));

        return _root;
    }   

    public void AddOrUpdateProperty(string propertyName, object propertyValue) { ...  }

    public bool RemoveProperty(string propertyName) { ... }

    public dynamic GetProperty(string propertyName) { ... }

    public IDictionary GetProperties() => _root as IDictionary;

}

public static class ExpandoExtension
{
    public static Expando ToExpando(this ExpandoObject expando)
    {
        return new Expando(expando); 
    }
}

这些只是一个草图,您可以实现甚至更好的东西,使它更适合于一个开放源码库,将是容易使用。对于这个扩展,我的想法是只需要一个扩展来返回一个新的Expando,这样您就可以强制使用类了。

另外,您可以定义一个私有Dictionary并使用它,而不是在每个方法上强制转换expandoObject,只需将结果存储在_root中。

我希望这将是有用的。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/238537

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档