2012-01-23 2 views
2

Привет, я не уверен, что общий метод - это правильный способ решить мою проблему. Мне нужно разобрать XML-файл и прочитать его. Элементы могут быть такими, как orderLines, заметки, вложения. Основные шаги для получения этих элементов - все те же. Как я могу создать 1 метод, который создает список этих элементов и вызывается конкретный метод для чтения элемента?Могу ли я использовать общий метод как шаблон шаблона?

public override IList<T> GetItems<T>(XPathNavigator currentOrder) where T : ISortableByLineNumber, new() 
    { 
     var itemList = new List<T>(); 
     var itemXmlNodes = currentOrder.Select(OrderXPath); 
     if (itemXmlNodes == null) 
      throw new Exception(""); 
     var lineNumber = 1; 
     foreach (XPathNavigator itemXmlNode in itemXmlNodes) 
     { 
      var item = new T(); 
      item = ReadItem(itemXmlNode, lineNumber++, item); 
      itemList.Add(item); 
      Logger.Debug(string.Format("Added item {0}", item)); 
     } 
     return itemList; 
    } 

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

private ISortableByLineNumber ReadItem(XPathNavigator itemXmlNode, int i, OrderLine item) 
    { 
     // specific code to read a orderLine 
    } 

    private ISortableByLineNumber ReadItem(XPathNavigator itemXmlNode, int i, Note item) 
    { 
     // specific code to read a note 
    } 

Но когда я пытаюсь скомпилировать это я мог бы получить «лучший перегруженный матч метод для„XmlOrderParser.XmlOrders.Prs3XmlFileWithOrders.ReadItem (System.Xml.XPath.XPathNavigator, INT, XmlOrderParser.Entities.OrderLine)“ имеет некоторые недопустимые аргументы ". Проблема заключается в том, что компилятор не знает, как отличать T от OrderLine или Note.

+0

ли классы: OrderLine , Примечание и другие реализуют интерфейс ISortableByLineNumber? – VS1

+0

Да, все они реализуют этот интерфейс. Я еще не вызываю функцию, поэтому это ошибка времени компиляции, а не ошибка времени выполнения. –

+0

, то я предполагаю, что вы должны использовать типы интерфейса в отличие от конкретных типов в параметрах метода, я написал ответ для этого. – VS1

ответ

2

Если вы используете .NET 4 вы можете использовать новый dynamic типа, изменив только одну вещь:

dynamic item = new T(); // instead of var item = new T(); 

Поскольку item сейчас dynamic среда выполнения автоматического разрешения перегрузки на основе фактического типа .
Обратите внимание, что вы получите исключение runtime, если T - тип, для которого не существует перегрузки.


Следующий фрагмент демонстрирует вашу проблему (вставить в LINQPad и выбрать «программу # C» в качестве языка):

void Main() 
{ 
    Method<Class1>(); // Outputs Class1 
    Method<Class2>(); // Outputs Class2 
    Method<Class2b>(); // Outputs Class2, because it falls back to the base type 
    Method<Class3>(); // Throws exception 
} 

void Method<T>() where T : new() 
{ 
    dynamic c = new T(); 
    Method(c); 
} 

void Method(Class1 c) { Console.WriteLine("Class1"); } 
void Method(Class2 c) { Console.WriteLine("Class2"); } 

class Class1 {} 
class Class2 {} 
class Class2b : Class2 {} 
class Class3 {} 
+1

Отлично, что, кажется, решает проблему! –

+0

@munnik: Отлично! Не забудьте принять мой ответ: [Как принять ответ] (http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work). –

+0

+1, хотя я бы добавил дополнительную информацию о том, почему нам нужно отложить его до времени выполнения, а именно тот факт, что компилятор не знает, является ли 'T' в' GetItems '' 'любой из' OrderLine' или 'Note' и т. Д. ., потому что ни одно общее ограничение не говорит об этом. – Krizz

0

Это потому, что во время компиляции вы не знаете, метод, который вы собираетесь необходимость. Чтобы решить эту проблему, вам нужно найти правильный метод и вызвать его. Попробуйте что-нибудь вроде:

MethodInfo mi = typeof(YourClass).GetMethod("ReadItem", 
    BindingFlags.NonPublic, 
    null, 
    new Type[] { typeof(XPathNavigator), typeof(int), typeof(T) }, 
    null); 
item = mi.Invoke(this, new object { itemXmlNode, lineNumber++, item }); 

Надеюсь, это поможет. Удачи!

+1

Это более или менее ручная транскрипция того, что делает 'dynamic'. Хорошая замена ответа Даниэля при использовании C# <4.0, хотя может возникнуть проблема, если T является подклассом типа аргумента некоторой перегрузки или я ошибаюсь? – Krizz

0

Как сказано в комментарии, все ваши классы OrderLine, примечание и т.д. реализовать интерфейс ISortableByLineNumber, так что вы можете изменить определение вашего метода ReadItem к:

private OrderLine ReadItem(XPathNavigator itemXmlNode, int i, ISortableByLineNumber item) 
{ 

} 

и фактический тип, который передается item паров в вышеуказанном способе будут иметь свою конкретную реализацию выполняется во время выполнения, так что если item имеет типа OrderLine, когда передаются от GetItems<T> метода, то его конкретная реализация будет ссылаться и так далее ..

че е выше изменений в определении методы сделает ваш интерфейс определения метода специфичны и не конкретный типа конкретным который является одним из практики interface based программирования объясняется ниже ссылки:

http://visualstudiomagazine.com/articles/2010/01/01/interface-based-programming.aspx

What exactly is "interface based programming"?

Смежные вопросы