【问题标题】:Accessing a Service from within an XNA Content Pipeline Extension从 XNA 内容管道扩展中访问服务
【发布时间】:2012-04-03 16:44:32
【问题描述】:

我需要允许我的内容管道扩展使用类似于工厂的模式。我从字典类型开始:

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 或等效项。

有什么想法吗?

【问题讨论】:

    标签: xna factory linq-to-xml pipeline


    【解决方案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 的帮助。

    【讨论】:

      猜你喜欢
      • 2012-02-11
      • 2012-07-04
      • 1970-01-01
      • 2011-04-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多