Доступ к службе из расширения конвейера содержимого XNA

Мне нужно разрешить моему расширению конвейера контента использовать шаблон, аналогичный фабрике. Я начинаю с типа словаря:

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

Вот один тип объекта, который я хочу сохранить:

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 — это метод расширения, который работает так, как можно было бы ожидать. Он возвращает логическое значение, которое истинно в случае успеха.

Проблема в том, что у меня есть много разных типов тестов, каждый из которых заполняет объект способом, уникальным для конкретного теста. Имя элемента является ключом к словарю MapFactory. Этот тип теста, хотя и нетипичный, иллюстрирует мою проблему.

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

Мой фактический код более сложен, так как фабрика имеет два словаря, один из которых превращает XElement в тип контента для записи, а другой используется читателем для создания реальных игровых объектов.

Мне нужно построить эти фабрики в коде, потому что они отображают строки в делегаты. У меня есть служба, которая содержит несколько таких фабрик. Задача состоит в том, чтобы сделать эти фабричные классы доступными для обработчика контента. Ни сам процессор, ни контекст, который он использует в качестве параметра, не имеют каких-либо известных ловушек для присоединения IServiceProvider или эквивалента.

Любые идеи?


person David Wallace    schedule 03.04.2012    source источник


Ответы (1)


Мне нужно было создать структуру данных практически по запросу без доступа к базовым классам, поскольку они были предоставлены третьей стороной, в данном случае XNA Game Studio. Я знаю только один способ сделать это... статически.

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()]);
        }
    }
}

Я пошел еще дальше и создал обработчики контента для каждого типа 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;
    }
}

Любая попытка сослаться на класс или получить к нему доступ, например вызов TestMap.Collect, при необходимости создаст базовый статический класс. Я в основном перенес код из LogicTest.Build в процессор. Я также выполняю любую необходимую проверку в процессоре.

Когда я доберусь до чтения этих классов, мне поможет ContentService.

person David Wallace    schedule 09.04.2012