优化大型 switch 语句

发布于 2024-11-09 07:07:23 字数 1252 浏览 5 评论 0原文

我有一个大型 switch 语句,其中我根据 XElement 的输入值创建 UIElement:

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

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

有什么办法可以避免这种重复吗?预先感谢您的想法。

I have large switch statement in which I create UIElements based on input value from XElement:

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

I am creating a lot controls like this and as you can see, too much repetition is going on.

Is there some way to avoid this repetition? Thanks in advance for ideas.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

不即不离 2024-11-16 07:07:23

您可以创建一个通用函数来执行以下操作:

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.

You could create a generic function that does the 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 }
}

Your switch then becomes:

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

You could also adapt this to pass in the XElement, whichever you prefer.

If the Type attribute is always the name of the System.Type you want, then you could just do

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

If there's a one to one mapping between the value of the attribute and the type you want, then you can declare a readonly static field with the mapping:

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

And use

UIElement elem = (UIElement) Activator.CreateInstance(uiTypeMapping[element.Attribute("Type").Value]);
etc.
海风掠过北极光 2024-11-16 07:07:23

像这样的东西可以工作......:)

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;

Something like this could work... :)

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;
死开点丶别碍眼 2024-11-16 07:07:23

这些不同的控件具有继承树。例如,宽度、高度、名称是在 FrameworkElement 上定义的。因此,您可以执行如下操作:

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 时,执行此操作”,即不适用于每种类型,但仅限于其中某些类型。

Those different controls have inheritance trees. So for example Width, Height, Name are defined on FrameworkElement. So you could do something like the following:

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;

Note that by using this approach, in comparison to Flynn's answer, you can also easily add code such as "when the control is an ItemsControl, do this", i.e. code which won't apply to every type, but only to some of them.

琉璃繁缕 2024-11-16 07:07:23

可以用反射+表达式来实现。

[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。留作练习。

You can do it with reflection + expressions.

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

To make it work with other types then string, you can use Expression.Convert. Left as an exercise.

我早已燃尽 2024-11-16 07:07:23

您可以使用反射来实现这一点,或者您可以创建一个字符串字典(您现在正在打开的字典)和您创建控件的 Funcs(或操作)。

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

You could to this with reflection instead, or you could create a Dictionary of strings (what you're switching on now) and Funcs (or actions rather) where you create the controls.

for the specific code you posted, you can assign height and width after the switch statement, since they exsist on Control directly.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文