【问题标题】:How do I make the method/s generic which returns different types, but accepts the same type of instance?如何使返回不同类型但接受相同类型实例的方法/ s 通用?
【发布时间】:2017-03-09 10:37:00
【问题描述】:

我有多种从 XML 读取数据的方法,格式如下:

<?xml version = "1.0" encoding="UTF-8" standalone="yes"?>
<document>
    <employee>
        <name>
            <lastname>Sample</lastname>
            <firstname>Test</firstname>
        </name>
        <professionalDetails>
            <hiredate>October 15, 2016</hiredate>
            <designation>Clerk</designation>
        </professionalDetails>
        <projects>
            <project>
                <product>Printer</product>
                <id>111</id>
                <price>$111.00</price>
            </project>
            <project>
                <product>Laptop</product>
                <id>222</id>
                <price>$989.00</price>
            </project>
        </projects>
    </employee>
</document>

要读取上述数据,我有以下方法及其各自的类。

  • 请注意,我有自己的“GetElementAsString”方法实现。请原谅我没有提供实现。

方法:

    private static NameDetails GetsNameDetails(XNode content)
    {
        var element = content.XPathSelectElement("document/employee/name");

        return new NameDetails
        {
            FirstName = element.GetElementAsString("firstName"),
            LastName = element.GetElementAsString("lastName")
        };
    }

    private static ProfessionalDetails GetsProfessionalDetailsDetails(XNode content)
    {
        var element = content.XPathSelectElement("document/employee/professionalDetails");

        return new ProfessionalDetails
        {
            HireDate = element.GetElementAsString("hiredate"),
            Designation = element.GetElementAsString("designation")
        };
    }

    private static Projects GetsProjectDetails(XNode content)
    {
        var element = content.XPathSelectElement("document/employee/projects/project");

        return new Projects
        {
            Id = element.GetElementAsString("id"),
            Price = element.GetElementAsString("price"),
            Product = element.GetElementAsString("product")
        };
    }
}

internal class Projects
{
    public int Id { get; set; }
    public string Product { get; set; }
    public string Price { get; set; }
}

internal class ProfessionalDetails
{
    public DateTime HireDate { get; set; }
    public string Designation { get; set; }
}

internal class NameDetails
{
    public string FirstName  { get; set; }
    public string LastName { get; set; }
}

两种方法的基本逻辑相同。初始化类型是唯一改变的东西。方法的返回值和要初始化的属性/字段发生变化,但参数保持不变。

我如何为以下方法使用一个通用方法并决定在运行时初始化的类型?

【问题讨论】:

  • 让我换一种说法。只有一个通用方法而不是多个具有相同逻辑的方法是一个好主意,这当然更简单

标签: c# asp.net .net xml generics


【解决方案1】:

您可以添加一个泛型类型,然后根据类型决定您要如何解析节点:

private static T GetsDetails<T>(XNode content)
{
    if(typeof(T) == typeof(ProjectDetails)){
        var element = content.XPathSelectElement("document/employee/projects/project");

        return (T)new Projects
        {
            Id = element.GetElementAsString("id"),
            Price = element.GetElementAsString("price"),
            Product = element.GetElementAsString("product")
        };
    }
    else if(typeof(T) == typeof(ProfessionalDetails){ ... } 
    else if (...) {...}
}

不过,我不推荐这种方法:您仍然需要进行类型检查,而且收获很少。您的初始实现要简单得多。

【讨论】:

    【解决方案2】:

    你可以这样写:

    private static T ThingFiller<T>(
        XNode context, 
        string elementStr,
        params Action<Func<string, string>, T>[] setters
    )
        where T : new()
    {
        var element = context.XPathSelectElement(elementStr);
        var t = new T();
    
        foreach (var setter in setters)
            setter(element.GetElementAsString, t);      
    
        return t;
    }
    
    private static NameDetails GetsNameDetails(XNode content)
    {
        return ThingFiller<NameDetails>(content, "document/employee/name",
            (func, nd) => nd.FirstName = func("firstName"),
            (func, nd) => nd.LastName = func("lastName")
        );
    }
    

    或者,您可以使用 XML 库并简单地定义如何通过属性和映射来填充字段。

    【讨论】:

      【解决方案3】:

      您可以创建一个属性来确定应该使用哪个 XPath 表达式。示例:

      [XPathSelector("document/employee/projects/project")]
      internal class Projects
      {
          public int Id { get; set; }
          public string Product { get; set; }
          public string Price { get; set; }
      }
      

      然后您可以通过反射使用它(代码未经测试,此处没有可用的 c# 编译器):

      private static T GetNode<T>(XNode content)
          : where T : new()
      {
          var selector = typeof(T).GetCustomProperty<XPathSelector>();
          var element = content.XPathSelectElement(selector.XPathSelector);
      
          var ret = new T();
      
          foreach (var child in element.Elements) {
              var property = typeof(T).GetProperties().Single(p => p.Name.ToLower() == child.Name.ToLower());
      
              property.SetValue(ret, child.GetElementAsString(child.Name));
          }   
      
          return ret;
      }
      

      【讨论】:

        猜你喜欢
        • 2021-08-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-02-05
        • 2012-07-05
        相关资源
        最近更新 更多