【问题标题】:Is it possible to auto-generate object initialization code from a runtime object with values?是否可以从具有值的运行时对象自动生成对象初始化代码?
【发布时间】:2017-03-24 07:18:39
【问题描述】:

也许是一个很长的机会,但如果存在的话,它会节省我一些时间。

更详细地解释。假设我有一个长 XML 文件和一个映射类。我想在运行测试之前测试东西并更改值。我可以通过编写用于初始化该映射类的 C# 代码来重新构建整个 XML 结构,但我想知道的是 - 我绝对必须这样做吗?

所以基本上我想在运行时将一个大的 XML 文件解析为一个对象,然后将初始化代码生成为一个字符串,我可以粘贴到某个地方。假设输入是:

<MyObject>
    <Prop1>a</Prop1>
    <Prop2>b</Prop2>
    <Prop2>c</Prop2>
</MyObject>

我想要一个这样的字符串,例如:

"new MyObject() 
{
    Prop1 = "a",
    Prop2 = "b",
    Prop3 = "c"
}"

【问题讨论】:

  • 您有 XML 的 XSD 文件吗?
  • 应该没问题,你只需要解析 XML 并覆盖一个构造函数来处理解析的数据
  • 如果您不想构建整个 xml 结构,我会反序列化为动态类型。
  • 我认为 XML 部分在这里并不重要。我想要的是以某种方式从运行时对象实例生成可以重现该对象实例的代码行。然后我可以将这些代码行粘贴到我的测试中以初始化该对象。这是我只会运行一次的事情。我只是想避免自己编写大量对象初始化代码,因为 XML 文件很大。
  • 你想要的叫做序列化。您碰巧想要 C# 代码形式的不寻常的序列化格式。据我所知,没有现成的解决方案。使用像 XML 或 JSON 这样的既定格式并对其进行反序列化而不是坚持在测试中使用纯 C# 会少得多。

标签: c# visual-studio initialization auto-generate


【解决方案1】:

我遇到了同样的问题,因为我的所有测试数据都存储为 XML。提到的 Visual Studio 扩展 (OmarElabd/ObjectExporter) 是一个好主意,但我需要在运行时,在单元测试执行期间从内存中的对象生成 C# 代码。

这是从原始问题演变而来的: https://www.nuget.org/packages/ObjectDumper.NET/

ObjectDumper.Dump(obj, DumpStyle.CSharp);从变量返回 C# 初始化程序代码。如果您发现问题,请告诉我,您可能想在 github 上报告。

【讨论】:

    【解决方案2】:

    您可能想查看此 Visual Studio extension

    不过,您可以使用下面的代码从简单的开始。取自answer。它不适用于非类型化对象,但它有帮助。

    PS:一旦我提出增强功能,我将更新此代码。

        public class ObjectInitGenerator
    {
        public static string ToObjectInitializer(Object obj)
        {
            var sb = new StringBuilder(1024);
    
            sb.Append("var x = ");
            sb = WalkObject(obj, sb);
            sb.Append(";");
    
            return sb.ToString();
        }
    
        private static StringBuilder WalkObject(Object obj, StringBuilder sb)
        {
            var properties = obj.GetType().GetProperties();
    
            var type = obj.GetType();
            var typeName = type.Name;
            sb.Append("new " + type.Name + " {");
    
            bool appendComma = false;
            DateTime workDt;
            foreach (var property in properties)
            {
                if (appendComma) sb.Append(", ");
                appendComma = true;
    
                var pt = property.PropertyType;
                var name = pt.Name;
    
                var isList = property.PropertyType.GetInterfaces().Contains(typeof(IList));
    
                var isClass = property.PropertyType.IsClass;
    
                if (isList)
                {
                    IList list = (IList)property.GetValue(obj, null);
                    var listTypeName = property.PropertyType.GetGenericArguments()[0].Name;
    
                    if (list != null && list.Count > 0)
                    {
                        sb.Append(property.Name + " = new List<" + listTypeName + ">{");
                        sb = WalkList(list, sb);
                        sb.Append("}");
                    }
                    else
                    {
                        sb.Append(property.Name + " = new List<" + listTypeName + ">()");
                    }
                }
                else if (property.PropertyType.IsEnum)
                {
                    sb.AppendFormat("{0} = {1}", property.Name, property.GetValue(obj));
                }
                else
                {
                    var value = property.GetValue(obj);
                    var isNullable = pt.IsGenericType && pt.GetGenericTypeDefinition() == typeof(Nullable<>);
                    if (isNullable)
                    {
                        name = pt.GetGenericArguments()[0].Name;
                        if (property.GetValue(obj) == null)
                        {
                            sb.AppendFormat("{0} = null", property.Name);
                            continue;
                        }
                    }
    
                    switch (name)
                    {
                        case "Int64":
                        case "Int32":
                        case "Int16":
                        case "Double":
                        case "Float":
                            sb.AppendFormat("{0} = {1}", property.Name, value);
                            break;
                        case "Boolean":
                            sb.AppendFormat("{0} = {1}", property.Name, Convert.ToBoolean(value) == true ? "true" : "false");
                            break;
                        case "DateTime":
                            workDt = Convert.ToDateTime(value);
                            sb.AppendFormat("{0} = new DateTime({1},{2},{3},{4},{5},{6})", property.Name, workDt.Year, workDt.Month, workDt.Day, workDt.Hour, workDt.Minute, workDt.Second);
                            break;
                        case "String":
                            sb.AppendFormat("{0} = \"{1}\"", property.Name, value);
                            break;
                        default:
                            // Handles all user classes, should likely have a better way
                            // to detect user class
                            sb.AppendFormat("{0} = ", property.Name);
                            WalkObject(property.GetValue(obj), sb);
                            break;
                    }
                }
            }
    
            sb.Append("}");
    
            return sb;
        }
    
        private static StringBuilder WalkList(IList list, StringBuilder sb)
        {
            bool appendComma = false;
            foreach (object obj in list)
            {
                if (appendComma) sb.Append(", ");
                appendComma = true;
                WalkObject(obj, sb);
            }
    
            return sb;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2011-05-11
      • 1970-01-01
      • 1970-01-01
      • 2012-02-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-06
      • 2011-09-11
      相关资源
      最近更新 更多