2012-04-03 11 views
0

Ich muss meiner Inhalts-Pipeline-Erweiterung erlauben, ein Muster zu verwenden, das einer Factory ähnelt. Ich beginne mit einem Wörterbuchtyp:Zugreifen auf einen Service über eine XNA-Inhalts-Pipeline-Erweiterung

public delegate T Mapper<T>(MapFactory<T> mf, XElement d); 

public class MapFactory<T> 
{ 
    Dictionary<string, Mapper<T>> map = new Dictionary<string, Mapper<T>>(); 

    public void Add(string s, Mapper<T> m) 
    { 
     map.Add(s, m); 
    } 

    public T Get(XElement xe) 
    { 
     if (xe == null) throw new ArgumentNullException(
      "Invalid document"); 
     var key = xe.Name.ToString(); 
     if (!map.ContainsKey(key)) throw new ArgumentException(
      key + " is not a valid key."); 
     return map[key](this, xe); 
    } 

    public IEnumerable<T> GetAll(XElement xe) 
    { 
     if (xe == null) throw new ArgumentNullException(
      "Invalid document"); 
     foreach (var e in xe.Elements()) 
     { 
      var val = e.Name.ToString(); 
      if (map.ContainsKey(val)) 
       yield return map[val](this, e); 
     } 
    } 
} 

Hier ist eine Art von Objekt, das ich gespeichert werden soll:

public partial class TestContent 
{ 
    // Test type 
    public string title; 

    // Once test if true 
    public bool once; 

    // Parameters 
    public Dictionary<string, object> args; 

    public TestContent() 
    { 
     title = string.Empty; 
     args = new Dictionary<string, object>(); 
    } 

    public TestContent(XElement xe) 
    { 
     title = xe.Name.ToString(); 
     args = new Dictionary<string, object>(); 
     xe.ParseAttribute("once", once); 
    } 
} 

XElement.ParseAttribute ist eine Erweiterung Methode, die funktioniert wie man erwarten könnte. Es gibt einen booleschen Wert zurück, der wahr ist, wenn er erfolgreich ist.

Das Problem ist, dass ich viele verschiedene Arten von Tests habe, von denen jede das Objekt in einer Weise füllt, die für den spezifischen Test einzigartig ist. Der Elementname ist der Schlüssel zum MapFactory-Wörterbuch. Diese Art von Test, obwohl atypisch, veranschaulicht mein Problem.

public class LogicTest : TestBase 
{ 
    string opkey; 
    List<TestBase> items; 

    public override bool Test(BehaviorArgs args) 
    { 
     if (items == null) return false; 
     if (items.Count == 0) return false; 
     bool result = items[0].Test(args); 
     for (int i = 1; i < items.Count; i++) 
     { 
      bool other = items[i].Test(args); 
      switch (opkey) 
      { 
       case "And": 
        result &= other; 
        if (!result) return false; 
        break; 
       case "Or": 
        result |= other; 
        if (result) return true; 
        break; 
       case "Xor": 
        result ^= other; 
        break; 
       case "Nand": 
        result = !(result & other); 
        break; 
       case "Nor": 
        result = !(result | other); 
        break; 
       default: 
        result = false; 
        break; 
      } 
     } 
     return result; 
    } 

    public static TestContent Build(MapFactory<TestContent> mf, XElement xe) 
    { 
     var result = new TestContent(xe); 
     string key = "Or"; 
     xe.GetAttribute("op", key); 
     result.args.Add("key", key); 
     var names = mf.GetAll(xe).ToList(); 
     if (names.Count() < 2) throw new ArgumentException(
       "LogicTest requires at least two entries."); 
     result.args.Add("items", names); 
     return result; 
    } 
} 

Mein eigentlicher Code ist mehr beteiligt als die Fabrik zwei Wörterbücher hat, eine, die eine XElement in einen Inhaltstyp wird zu schreiben und ein andere durch den Leser verwendet, um die eigentlichen Spiel-Objekte zu erstellen.

Ich muss diese Fabriken in Code erstellen, da sie Zeichenfolgen Delegaten zuordnen. Ich habe einen Service, der mehrere dieser Fabriken enthält. Die Aufgabe besteht darin, diese Factory-Klassen einem Content-Prozessor zur Verfügung zu stellen. Weder der Prozessor selbst noch der Kontext, den er als Parameter verwendet, haben irgendwelche bekannten Hooks, um einen IServiceProvider oder ein Äquivalent anzuhängen.

Irgendwelche Ideen?

Antwort

0

Ich musste eine Datenstruktur im Wesentlichen auf Nachfrage ohne Zugriff auf die zugrunde liegenden Klassen erstellen, wie sie von einer dritten Partei kamen, in diesem Fall XNA Game Studio. Es gibt nur einen Weg, dies zu tun, von dem ich weiß ... statisch.

public class TestMap : Dictionary<string, string> 
{ 
    private static readonly TestMap map = new TestMap(); 

    private TestMap() 
    { 
     Add("Logic", "LogicProcessor"); 
     Add("Sequence", "SequenceProcessor"); 
     Add("Key", "KeyProcessor"); 
     Add("KeyVector", "KeyVectorProcessor"); 
     Add("Mouse", "MouseProcessor"); 
     Add("Pad", "PadProcessor"); 
     Add("PadVector", "PadVectorProcessor"); 
    } 

    public static TestMap Map 
    { 
     get { return map; } 
    } 

    public IEnumerable<TestContent> Collect(XElement xe, ContentProcessorContext cpc) 
    { 
     foreach(var e in xe.Elements().Where(e => ContainsKey(e.Name.ToString()))) 
     { 
      yield return cpc.Convert<XElement, TestContent>(
       e, this[e.Name.ToString()]); 
     } 
    } 
} 

Ich nahm einen dieser Schritt weiter und erstellt Content-Prozessoren für jede Art von Testbase:

/// <summary> 
/// Turns an imported XElement into a TestContent used for a LogicTest 
/// </summary> 
[ContentProcessor(DisplayName = "LogicProcessor")] 
public class LogicProcessor : ContentProcessor<XElement, TestContent> 
{ 
    public override TestContent Process(XElement input, ContentProcessorContext context) 
    { 
     var result = new TestContent(input); 
     string key = "Or"; 
     input.GetAttribute("op", key); 
     result.args.Add("key", key); 
     var items = TestMap.Map.Collect(input, context); 
     if (items.Count() < 2) throw new ArgumentNullException(
       "LogicProcessor requires at least two items."); 
     result.args.Add("items", items); 
     return result; 
    } 
} 

Jeder Versuch TestMap.Collect zu verweisen oder die Klasse zugreifen wie Aufruf wird die zugrunde liegende statische Klasse erzeugen wenn benötigt. Ich habe den Code von LogicTest.Build in den Prozessor verschoben. Ich führe auch jede notwendige Validierung im Prozessor durch.

Wenn ich diese Klassen lese, werde ich den ContentService haben, um zu helfen.