我在WPF应用程序中有以下代码
if (panel != null)
{
IList listOfValues = new ComparableListOfObjects();
var childControls = panel.GetChildren<Control>(x => x.Visibility == Visibility.Visible);
foreach (Control childControl in childControls)
{
var textBox = childControl as TextBox;
if (textBox != null)
{
listOfValues.Add(textBox.Text);
continue;
}
var comboBox = childControl as ComboBox;
if (comboBox != null)
{
listOfValues.Add(comboBox.SelectedItem);
continue;
}
var datePicker = childControl as DatePicker;
if (datePicker != null)
{
listOfValues.Add(datePicker.SelectedDate.GetValueOrDefault());
continue;
}
var numericBox = childControl as NumericUpDown;
if (numericBox != null)
{
listOfValues.Add(numericBox.Value);
continue;
}
}
重构这个代码的最佳方法是什么,重复相同的逻辑,从不同的控件中提取值,如?
var numericBox = childControl as NumericUpDown;
if (numericBox != null)
{
listOfValues.Add(numericBox.Value);
continue;
}
在其他方法的同一个类中有相同的代码
private static object GetControlValue(Control control)
{
if (control == null)
throw new ArgumentNullException("control");
var textBox = control as TextBox;
if (textBox != null)
return textBox.Text;
var comboBox = control as ComboBox;
if (comboBox != null)
return comboBox.SelectedValue;
var datePicker = control as DatePicker;
if (datePicker != null)
return datePicker.SelectedDate.GetValueOrDefault();
var numericUpDown = control as NumericUpDown;
if (numericUpDown != null)
return numericUpDown.Value;
throw new NotSupportedException();
}
我可以使用策略设计模式,但在这种情况下,我需要为每种类型的控件创建其他类吗?
你能否建议我更好地适应这个问题? 提前谢谢。
答案 0 :(得分:6)
拥有if
和switch
语句并不是件坏事。即使进行一些基本类型检查也不一定是坏事,特别是当使用的类型不能以多态方式使用时。让这个逻辑不止一次地表达是不赞成的,因为你正在重复自己,并且你有相同变化的多个维护点。
在原始代码段中,您可以
var blah = obj as Foo;
if (blah != null)
{
someList.Add(blah.Value);
}
对于其他几种控件类型重复此操作。但是后来在你的私有方法中,你基本上有相同的逻辑表示相同的次数。
var blah = obj as Foo;
if (blah != null)
return blah.Value;
唯一的区别是,在第一个代码段中,您获取值并将其添加到列表中。在第二个中,您返回值。第一个代码片段取消及其类型检查逻辑,它应该使用已经在另一个方法中表达的逻辑。
foreach (var control in childControls)
{
listOfValues.Add(GetControlValue(control));
}
这个想法是不要重复自己。 DRY。
答案 1 :(得分:1)
我相信你正在寻找Visitor pattern。每个控制器一个类是一种方法,但引用引用的文章:
注意:更灵活的方法是创建一个包装器 实现定义accept方法的接口的类。该 包装器包含一个指向CarElement的引用,它可以是 通过构造函数初始化。这种方法避免了必须 在每个元素上实现一个接口。 [见文章Java技巧98 下面的文章]
你或许可以逃脱这个。
答案 2 :(得分:1)
有点黑客攻击,但是这里有一种方法可以使用委托和集合初始化程序来消除冗余(你可能不愿意按原样使用它,而是想法)。
首先创建一个这样的类:
// Needs argument validation. Also, extending Dictionary<TKey, TValue>
// probably isn't a great idea.
public class ByTypeEvaluator : Dictionary<Type, Func<object, object>>
{
public void Add<T>(Func<T, object> selector)
{
Add(typeof(T), x => selector((T)x));
}
public object Evaluate(object key)
{
return this[key.GetType()](key);
}
}
然后用法变为:
// Give this variable longer lifetime if you prefer.
var map = new ByTypeEvaluator
{
(ComboBox c) => c.SelectedItem,
(TextBox t) => t.Text,
(DateTimePicker dtp) => dtp.Value,
(NumericUpDown nud) => nud.Value
};
Control myControl = ...
var myProjection = map.Evaluate(myControl);
答案 3 :(得分:1)
您可以在通用方法中将其作为案例选择执行,但仍有一些使用此样式的工作:
public static string GetValue<T>(T obj) where T:Control
{
switch (obj.GetType().ToString())
{
case "TextBox":
return (obj as TextBox).Text;
break;
case "ComboBox":
return (obj as ComboBox).SelectedValue.ToString();
break;
//..etc...
}
}
答案 4 :(得分:-1)
你可以使用这样的方法,依靠is
:
private static object GetControlValue(Control control)
{
if (control == null)
throw new ArgumentNullException("control");
if (control is TextBox) return (control as TextBox).Text;
if (control is ComboBox) return (control as ComboBox).SelectedValue;
...
}
和
if (panel != null)
{
IList listOfValues = new ComparableListOfObjects();
var childControls = panel.GetChildren<Control>(x => x.Visibility == Visibility.Visible);
foreach (Control childControl in childControls)
{
if(childControl is TextBox) { listOfValues.Add((childControl as TextBox).Text); continue; }
if(childControl is ComboBox) { listOfValues.Add((childControl as ComboBox).SelectedValue); continue; }
...
}
}
第二个区块中的continue
可能甚至不需要,但需要进行一些测试。