优化大型switch语句

时间:2011-05-23 08:32:32

标签: c# .net-4.0 switch-statement

我有一个大的switch语句,我根据XElement的输入值创建UIElements:

public static UIElement CreateElement(XElement element) {
            var name = element.Attribute("Name").Value;
            var text = element.Attribute("Value").Value;
            var width = Convert.ToDouble(element.Attribute("Width").Value);
            var height = Convert.ToDouble(element.Attribute("Height").Value);
            //...
            switch (element.Attribute("Type").Value) {
                case "System.Windows.Forms.Label":
                    return new System.Windows.Controls.Label() {
                        Name = name,
                        Content = text,
                        Width = width,
                        Height = height
                    };
                case "System.Windows.Forms.Button":
                    return new System.Windows.Controls.Button() {
                        Name = name,
                        Content = text,
                        Width = width,
                        Height = height
                    };
                    //...
                default:
                    return null;
            }
        }

我正在创建这样的很多控件,正如您所看到的,重复过多。

有没有办法避免这种重复?提前感谢您的想法。

5 个答案:

答案 0 :(得分:6)

您可以创建执行create:

的通用函数
private static Create<T>(string name, string text, double width, double height) where T: Control, new()
{
   return new T { Name = name, Content = text, Width = width, Height = height }
}

您的开关变为:

switch (element.Attribute("Type").Value) {
  case "System.Windows.Forms.Label" : return Create<System.Windows.Forms.Label>(name, text, width, height);
  etc.
}

您也可以根据自己的喜好调整此项以传递XElement。

如果Type属性始终是您想要的System.Type的名称,那么您可以执行

Control ctrl = (Control) Activator.CreateInstance(Type.GetType(element.Attribute("Type").Value));
ctrl.Name = name;
etc.

如果属性值与所需类型之间存在一对一映射,则可以使用映射声明只读静态字段:

private static readonly uiTypeMapping = new Dictionary<string,Type> {
  { "System.Windows.Forms.Label", typeof(System.Windows.Controls.Label) },
  { "System.Windows.Forms.Button", typeof(System.Windows.Controls.Button) },
  { etc. }
};

并使用

UIElement elem = (UIElement) Activator.CreateInstance(uiTypeMapping[element.Attribute("Type").Value]);
etc.

答案 1 :(得分:6)

像这样的东西可以起作用...... :)。

var controlCreators = new Dictionary<string, Func<ContentControl>>
                        {
                            {"System.Windows.Forms.Label", () => new Label()},
                            {"System.Windows.Forms.Button", () => new Button()}
                        };

Func<ContentControl> createControl;
if (!controlCreators.TryGetValue(element.Attribute("Type").Value, out createControl))
{
    return null;
}

var control = createControl();
control.Name = name;
control.Content = text;
control.Width = width;
control.Height = height;
return control;

答案 2 :(得分:1)

那些不同的控件有继承树。例如,在FrameworkElement上定义了Width,Height,Name。所以你可以做类似以下的事情:

object createdObject = null;
switch (element.Attribute("Type").Value)
{
case "System.Windows.Forms.Label":
    createdObject = new System.Windows.Controls.Label();
    break;
case "System.Windows.Forms.Button":
    createdObject = new System.Windows.Controls.Button();
    break;
}

var fe = createdObject as FrameworkElement;
if (fe != null)
{
    fe.Name = element.Attribute("Name").Value;
    fe.Width = Convert.ToDouble(element.Attribute("Width").Value);
    fe.Height = Convert.ToDouble(element.Attribute("Height").Value);
}

var ce = createdObject as ContentElement;
if (ce != null)
{
     ce.Content = element.Attribute("Value").Value;
}

return createdObject;

请注意,通过使用这种方法,与Flynn的答案相比,您还可以轻松添加代码,例如“当控件是ItemsControl时,执行此操作”,即代码不适用于所有类型,但仅适用于其中一些。

答案 3 :(得分:1)

你可以用反射+表达式来完成。

[TestClass]
public class UnitTest1
{
    public class Creator
    {
        private static Dictionary<string,Func<XElement, Control>> _map = new Dictionary<string, Func<XElement,Control>>();

        public static Control Create(XElement element)
        {
            var create = GetCreator(element.Attribute("Type").Value);

            return create(element);
        }

        private static Expression<Func<XElement, string>> CreateXmlAttributeAccessor(string elementName)
        {
            return (xl => xl.Attributes(elementName).Select(el => el.Value).FirstOrDefault() ?? "_" + elementName);
        }

        private static Func<XElement, Control> GetCreator(string typeName)
        {
            Func<XElement, Control> existing;
            if (_map.TryGetValue(typeName, out existing))
                return existing;

            // mapping for whatever property names you wish
            var propMapping = new[]
            {
                new{ Name = "Name", Getter = CreateXmlAttributeAccessor("Name") },
                new{ Name = "Content", Getter = CreateXmlAttributeAccessor("Value") },
            };

            var t = Assembly.GetAssembly(typeof (Control)).GetType("System.Windows.Controls." + typeName);

            var elementParameter = Expression.Parameter(typeof (XElement), "element");

            var p = from propItem in propMapping
                    let member = t.GetMember(propItem.Name)
                    where member.Length != 0
                    select (MemberBinding)Expression.Bind(member[0], Expression.Invoke(propItem.Getter, elementParameter));

            var expression = Expression.Lambda<Func<XElement, Control>>(
                Expression.MemberInit(Expression.New(t),p), elementParameter);

            existing = expression.Compile();
            _map[typeName] = existing;

            return existing;
        }
    }

    [TestMethod]
    public void TestMethod1()
    {
        var xel = new XElement("control",
            new XAttribute("Type", "Button"),
            new XAttribute("Name", "Foo"),
            new XAttribute("Value", "Bar"),
            new XElement("NonExistent", "foobar")); // To check stability

        var button = (Button) Creator.Create(xel);

        Assert.AreEqual("Foo", button.Name);
        Assert.AreEqual("Bar", button.Content);
    }
}

要使其与其他类型的字符串一起使用,您可以使用Expression.Convert。留下来作为练习。

答案 4 :(得分:0)

您可以使用反射代替,或者您可以创建字符串字典(您现在要切换的内容)和创建控件的Func(或操作)。

对于您发布的特定代码,您可以在switch语句后指定高度和宽度,因为它们直接在Control上存在。