1

我有一個從XML反序列化的模型,其中所有節點對象都從相同的基類派生,並且節點可以任意嵌套(有點)。我正在嘗試編寫一個模塊集合,可以將加載的模型轉換爲各種基於文本的格式。我認爲如果每個這樣的模塊都是一個擴展類,可以讓我簡單地調用Model.ToText(),Model.ToHtml()等,那麼它會很漂亮。但是我遇到了一些問題。在迭代基類集合時解決子類擴展方法

下面是一個簡單的例子:如果我運行這個

using System; 
using System.Collections.Generic; 
using System.Text; 

namespace Sample 
{ 
    public abstract class Foo 
    { 
    } 

    public class Bar : Foo 
    { 
     public List<Foo> Children = new List<Foo>(); 
     public int Qux; 
    } 

    public class Baz : Foo 
    { 
     public string Quux; 
    } 

    public static class Extension 
    { 
     public static string ToText(this Foo foo, int indent = 0) 
     { 
      return String.Format("{0}Foo <>\n", new String(' ', indent)); 
     } 

     public static string ToText(this Bar bar, int indent=0) 
     { 
      StringBuilder sb = new StringBuilder(); 
      sb.Append(String.Format("{0}Bar <Qux={1}>\n", new String(' ', indent), bar.Qux)); 
      foreach (var child in bar.Children) 
      { 
       sb.Append(child.ToText(indent + 1)); 
      } 
      return sb.ToString(); 
     } 

     public static string ToText(this Baz baz, int indent=0) 
     { 
      return String.Format("{0}Baz <Quux={1}>\n", new String(' ', indent), baz.Quux); 
     } 

    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      Baz baz = new Baz { Quux = "frob" }; 

      Bar bar = new Bar 
       { 
        Children = new List<Foo>() 
         { 
          new Baz {Quux = "fred"}, 
          new Bar 
           { 
            Qux = 11, 
            Children = new List<Foo>() 
             { 
              new Baz() {Quux = "flog"} 
             } 
           } 
         } 
       }; 

      //This works 
      Console.WriteLine(baz.ToText()); 

      //But this doesn't 
      Console.WriteLine(bar.ToText()); 
      Console.ReadKey(); 
     } 
    } 
} 

我得到:

Baz <Quux=frob> 

Bar <Qux=0> 
Foo <> 
Foo <> 

如果我嘗試並獲得棘手,而是執行此操作:

public static string ToText(this Foo foo, int indent = 0) 
{ 
    return ((dynamic)foo).ToText(indent); 
} 

。 ..第一印刷作品,但第二個給我的例外:

{"'Sample.Baz' does not contain a definition for 'ToText'"} 

我可能完全採取了錯誤的方法,但我可以使用一些方向。

+0

作爲獎勵,這將是很高興把大部分的工作交給一個適當的模板引擎。我一直在修補RazorEngine,但是我在處理模型的遞歸性質時遇到了麻煩。 – qSlug

回答

1

絆倒了一下後,我發現主題Virtual Extension Methods?。它看起來像我可以乾淨地Visitor模式解決這個問題:

public interface IFooFormater 
{ 
    string Format(Foo foo, int indent); 
    string Format(Bar bar, int indent); 
    string Format(Baz baz, int indent); 
} 

public class FooFormater : IFooFormater 
{ 
    public string Format(Foo foo, int indent) 
    { 
     return ""; 
    } 

    public string Format(Bar bar, int indent) 
    { 
     StringBuilder sb = new StringBuilder(); 
     sb.Append(String.Format("{0}Bar <Qux={1}>\n", new String(' ', indent), bar.Qux)); 
     foreach (var child in bar.Children) 
     { 
      sb.Append(this.Format((dynamic)child , indent + 1)); 
     } 
     return sb.ToString();    
    } 

    public string Format(Baz baz, int indent) 
    { 
     return String.Format("{0}Baz <Quux={1}>\n", new String(' ', indent), baz.Quux); 
    } 
} 


public static class Extension 
{ 

    public static string ToText(this Foo foo, IFooFormater fooFormater) 
    { 
     return fooFormater.Format((dynamic)foo, 0); 
    } 
} 

通過調用然後:

IFooFormater fooFormater = new FooFormater(); 
Console.WriteLine(bar.ToText(fooFormater)); 

我得到預期的輸出:

Bar <Qux=0> 
Baz <Quux=fred> 
Bar <Qux=11> 
    Baz <Quux=flog>