挑战:您可以使用C#4.0使这个简单的功能更加优雅

Pre*_*gha 10 xml c#-4.0

当我破解我们的代码库时,我才注意到这个函数.它将IDictionary<string, object>(Paramters - 一个实例变量)转换为XML字符串.

这只不过是我的好奇心:-).

那么使用C#4.0可以用更少的代码编写吗?规则:除了.Net Framework BCL之外没有外部库.

为了使它更具挑战性,我不会在这里输入输入字典规范,因为你应该能够从代码中解决它.

public string ConvertToXml() {
    XmlDocument doc = new XmlDocument();
    doc.LoadXml("<?xml version='1.0' encoding='utf-8'?><sc/>");
    foreach (KeyValuePair<string, object> param in Parameters) {
        XmlElement elm = doc.CreateElement("pr");

        if (param.Value is int || param.Value is Int32 || param.Value is Int16 || param.Value is Int64) {
            elm.SetAttribute("tp", "int");
        } else if (param.Value is DateTime?){
            elm.SetAttribute("tp", "datetime");
        } else {
            elm.SetAttribute("tp", "string");
        }

        elm.SetAttribute("nm", param.Key);
        if (param.Value is DateTime?) {
            DateTime? dateTime = param.Value as DateTime?;
            elm.SetAttribute("vl", dateTime.Value.ToString("o"));
        } else{
            elm.SetAttribute("vl", param.Value.ToString());
        }
        doc.FirstChild.NextSibling.AppendChild(elm);
    }
    return doc.OuterXml;
}
Run Code Online (Sandbox Code Playgroud)

让我补充一些想法.

对我来说 :

  • 少即是多,但简洁就是坏
  • 更多类型很好,但琐碎的类型似乎很臭
  • 可重用性很好

Jef*_*ado 8

使用LINQ to XML可以使编写起来非常简单.如果您有选择,请优先使用标准XML库.

我相信这应该是等价的:

public static string ToXmlString(this IDictionary<string, object> dict)
{
    var doc = new XDocument(new XElement("sc", dict.Select(ToXElement)));

    using (var writer = new Utf8StringWriter())
    {
        doc.Save(writer); // "hack" to force include the declaration
        return writer.ToString();
    }
}

class Utf8StringWriter : StringWriter
{
    public override Encoding Encoding { get { return Encoding.UTF8; } }
}

static XElement ToXElement(KeyValuePair<string, object> kvp)
{
    var value = kvp.Value ?? String.Empty;

    string typeString;
    string valueString;
    switch (Type.GetTypeCode(value.GetType()))
    {
    case TypeCode.Int16:
    case TypeCode.Int32:
    case TypeCode.Int64:
        typeString = "int";
        valueString = value.ToString();
        break;
    case TypeCode.DateTime:
        typeString = "datetime";
        valueString = ((DateTime)value).ToString("o");
        break;
    default:
        typeString = "string";
        valueString = value.ToString();
        break;
    }

    return new XElement("pr",
        new XAttribute("tp", typeString),
        new XAttribute("nm", kvp.Key),
        new XAttribute("vl", valueString));
}
Run Code Online (Sandbox Code Playgroud)

请注意,检查值是否类型DateTime?是没有意义的.我不确定null在字典中存储值有什么价值但是如果可能的话,你会因为制作类型的值而失去那种类型的信息object.

此外,如果有一个DateTime?值不是null,那么值本身将被装箱,而不是Nullable<DateTime>结构本身.所以实际的类型将DateTime是这个代码工作的原因.


Kev*_*vin 6

使用动态和LINQ to XML:

ConvertToXml可以简化为一个语句(假设省略XML声明是可以接受的).

public string ConvertToXml()
{
    return new XElement("sc",
        Parameters.Select(param => CreateElement(param.Key, (dynamic)param.Value))
    ).ToString(SaveOptions.DisableFormatting);
}
Run Code Online (Sandbox Code Playgroud)

请注意,CreateElement将param.Value强制转换为动态,以便在运行时选择以下正确的重载.

XElement CreateElement(string key, object value)
{
    return CreateElement("string", key, value.ToString());
}

XElement CreateElement(string key, long value)
{
    return CreateElement("int", key, value.ToString());
}

XElement CreateElement(string key, DateTime value)
{
    return CreateElement("datetime", key, value.ToString("o"));
}
Run Code Online (Sandbox Code Playgroud)

上面的重载最终会调用:

XElement CreateElement(string typename, string key, string value)
{
    return new XElement("pr",
        new XAttribute("tp", typename),
        new XAttribute("nm", key),
        new XAttribute("vl", value)
    );
}
Run Code Online (Sandbox Code Playgroud)

此代码减少了问题中的语句数(尽管不是行).这种方法建立在svick的基础之上,但减少了所需的方法和动态调用的数量.


jbt*_*ule 5

使用.net 4.0等功能Tupledynamic关键字.我的测试用例产生了原始问题的精确输出.

//using System;
//using System.Collections.Generic;
//using System.Linq;
//using System.Xml.Linq;

    public string ConvertToXml()
    {
        //Create XDocument to specification with linq-to-xml
        var doc = new XDocument(
            new XElement("sc",
                    from param in Parameters
                    //Uses dynamic invocation to use overload resolution at runtime
                    let attributeVals = AttributeValues((dynamic)param.Value)
                    select new XElement("pr",
                                new XAttribute("tp", attributeVals.Item1),
                                new XAttribute("nm", param.Key),
                                new XAttribute("vl", attributeVals.Item2)
                           )
            )
        );
        //Write to string
        using (var writer = new Utf8StringWriter())
        {
            doc.Save(writer, SaveOptions.DisableFormatting);//Don't add whitespace
            return writer.ToString();
        }
    }
    //C# overloading will choose `long` as the best pick for `short` and `int` types too
    static Tuple<string, string> AttributeValues(long value)
    {
        return Tuple.Create("int", value.ToString());
    }
    //Overload for DateTime
    static Tuple<string, string> AttributeValues(DateTime value)
    {
        return Tuple.Create("datetime", value.ToString("o"));
    }
    //Overload catch all
    static Tuple<string, string> AttributeValues(object value)
    {
        return Tuple.Create("string", value.ToString());
    }
    // Using John Skeet's Utf8StringWriter trick
    // http://stackoverflow.com/questions/3871738/force-xdocument-to-write-to-string-with-utf-8-encoding/3871822#3871822
    class Utf8StringWriter : System.IO.StringWriter
    {
        public override System.Text.Encoding Encoding { get { return System.Text.Encoding.UTF8; } }
    }
Run Code Online (Sandbox Code Playgroud)

可选:将 let语句更改为:

let attributeVals = (Tuple<string,string>)AttributeValues((dynamic)param.Value)
Run Code Online (Sandbox Code Playgroud)

这会将动态调用限制在该行.但由于没有其他事情发生,我认为不想添加额外的演员会更干净.


svi*_*ick 5

public string ConvertToXml()
{
    var doc = new XDocument(
        new XElement("sd",
            Parameters.Select(param =>
                new XElement("pr",
                    new XAttribute("tp", GetTypeName((dynamic)param.Value)),
                    new XAttribute("nm", param.Key),
                    new XAttribute("vl", GetValue((dynamic)param.Value))
                    )
                )
            )
        );
    return doc.ToString();
}
Run Code Online (Sandbox Code Playgroud)

此代码假定您已重载方法GetTypeName()GetValue()实现为:

static string GetTypeName(long value)
{
    return "int";
}

static string GetTypeName(DateTime? value)
{
    return "datetime";
}

static string GetTypeName(object value)
{
    return "string";
}

static string GetValue(DateTime? value)
{
    return value.Value.ToString("o");
}

static string GetValue(object value)
{
    return value.ToString();
}
Run Code Online (Sandbox Code Playgroud)

这使用了这样的事实:在使用时dynamic,将在运行时选择正确的重载.

你并不需要过载intshort,因为它们可以被转换成long(并且这样的转换被认为是比转换,以更好object).但是,这也意味着,类型,如ushortbyte将得到tpint.

此外,返回的字符串不包含XML声明,但无论如何声明UTF-16编码的字符串是UTF-8编码也没有意义.(如果您希望稍后将其保存在UTF-8编码文件中,则返回并保存XDocument将更好,并将编写正确的XML声明.)

我觉得这是一个很好的解决方案,因为它很好地分离问题纳入不同的方法(你甚至可以把GetTypeName()GetValue()超载成不同的类).