2016-10-26 3 views
0

У меня есть несколько методов, которые считывают данные из XML, имеющие следующий формат:Как создать метод/s generic, который возвращает разные типы, но принимает тот же тип экземпляра?

<?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; } 
} 

Основная логика одинакова для обоих методов. Инициализирующий тип - это единственное, что меняется. Возвращаемое значение метода и свойства/поля, подлежащие инициализации, изменяются, но параметр остается неизменным.

Как получить один общий метод для нижеописанных методов и решить, какой тип будет инициализирован во время выполнения?

+0

Пусть мне поставить выше по-другому. Было бы неплохо иметь только один общий метод вместо нескольких методов с одинаковой логикой, который, конечно, проще –

ответ

1

Вы могли бы написать что-то вроде этого:

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 и просто определить, как заполнять поля с помощью атрибутов и отображения.

0

Вы можете добавить общий тип, а затем в зависимости от типа решить, как вы хотите, чтобы разобрать узел:

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 (...) {...} 
} 

Я бы не рекомендовал этот подход, хотя вы все еще должны сделать проверку типов и вы получаете очень мало. Начальная реализация у вас намного проще.

0

Вы можете создать атрибут, определяющий, какое выражение 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; 
} 
Смежные вопросы