我想重构以下递归方法:
public static void Initialize(Control control, DocumentContainer container, ErrorProvider provider)
{
if (control == null)
{
return;
}
var controlWithTextBase = control as ICustomControlWithText;
if (controlWithTextBase != null)
{
controlWithTextBase.DocumentLoaded = true;
controlWithTextBase.Initialize(container, provider);
}
var custom = control as CustomCheckbox;
if (custom != null)
{
custom.DocumentLoaded = true;
custom.Initialize(container);
}
foreach (Control subControl in control.Controls)
{
Initialize(subControl, container, provider);
}
}
public interface ICustomControlWithText : ICustomControl
{
void Initialize(DocumentContainer container, ErrorProvider provider);
void InitializeValidations();
string Text { get; set; }
ErrorProvider ErrorProvider { get; set; }
List<IValidation> Validations { get; set; }
}
public interface ICustomControl
{
void Clear();
FieldType FieldType { get; set; }
bool DocumentLoaded { get; set; }
}
class CustomCheckbox : CheckBox, ICustomControl
{
public void Initialize(DocumentContainer container)
{
//...
}
}
如您所见,取决于winforms的类型控制此代码初始化控件。它从主窗体开始,包含自定义控件( IControlWithText , CustomCheckbox )和默认的winforms窗体。 我会创建3个初始化器和每个方法CanInitialize,具体取决于控件的类型,但即便如此,我也不知道如何跳过那些&#34; ifs&#34;,如果需要发送,我需要知道< strong> ErrorProvider 到方法Initialize。
我非常感谢你的帮助!
答案 0 :(得分:4)
您可以使用&#34;动态重载解析&#34;。 (需要.Net 4+)
如果将输入控件转换为dynamic
,。Net将在运行时查找合适的过载
小心提供一个&#34; catch&#34;在意外类型的控制情况下过载。这就是object
重载在这里的好处。否则您可能会遇到运行时异常。
public static void Initialize(Control control, DocumentContainer container, ErrorProvider provider)
{
if (control == null) return;
dynamic c = control;
InitializeControl(c, container, provider);
foreach (Control subControl in control.Controls)
Initialize(subControl, container, provider);
}
public static void InitializeControl(ICustomControlWithText controlWithTextBase, DocumentContainer container, ErrorProvider provider)
{
controlWithTextBase.DocumentLoaded = true;
controlWithTextBase.Initialize(container, provider);
}
public static void InitializeControl(CustomCheckbox custom, DocumentContainer container, ErrorProvider provider)
{
custom.DocumentLoaded = true;
custom.Initialize(container);
}
public static void InitializeControl(object _, DocumentContainer container, ErrorProvider provider)
{
// do nothing if the control is neither a ICustomControlWithText nor a CustomCheckbox
}
答案 1 :(得分:3)
您正在寻找的是Visitor(Gang of Four)模式。
确保您的基准界面ICustomControl
通过向其添加额外的Accept
方法来接受访问者。让该访问者被命名为ControlVisitor
,但任何其他名称都可以。
public interface ICustomControl
{
void Accept(ControlVisitor visitor);
void Clear();
FieldType FieldType { get; set; }
bool DocumentLoaded { get; set; }
}
简化Initialize
方法
public static void Initialize(Control control, ControlVisitor visitor)
{
if (control == null) //can this ever be null?
{
return;
}
var customControl = control as ICustomControl;
if (customControl != null)
{
customControl.Accept(visitor);
}
foreach (Control subControl in control.Controls)
{
Initialize(subControl, visitor);
}
}
通过添加访问者填写空白(我的ControlVisitor
示例是一个具体的例子,但你也可以为它设置一个界面)。在这里,您将提供一个重载的Visit
方法
public class ControlVisitor
{
private readonly DocumentContainer container;
private readonly ErrorProvider provider;
public ControlVisitor(DocumentContainer container, ErrorProvider provider)
{
this.container = container;
this.provider = provider;
}
public void Visit(ICustomControlWithText control)
{
control.DocumentLoaded = true;
control.Initialize(container, provider);
}
public void Visit(CustomCheckbox control)
{
control.DocumentLoaded = true;
control.Initialize(container);
}
}
Accept方法的实现非常简单,无论你在哪里都一样。下面是CustomCheckBox
的示例:
public class CustomCheckbox : CheckBox, ICustomControl
{
//..
public void Accept(ControlVisitor visitor)
{
visitor.Visit(this);
}
//..
}
答案 2 :(得分:1)
我同意@ 3dGrabber,但这里还有一个解决方案,不需要动态,但与3dGrabber的答案非常相似。但严格来说,旧版的.net 。
public class ClassInitiator{
public static void Initialize(Control control,
DocumentContainer container, ErrorProvider provider)
{
if (control == null) return;
typeof(ClassInitiator).InvokeMember(
"InitializeControl",
BindingFlags.InvokeMethod | BindingFlags.Public,
null,
null,
new object[]{
control,
container,
provider
});
foreach (Control subControl in control.Controls)
Initialize(subControl, container, provider);
}
public static void InitializeControl(
ICustomControlWithText controlWithTextBase,
DocumentContainer container,
ErrorProvider provider)
{
controlWithTextBase.DocumentLoaded = true;
controlWithTextBase.Initialize(container, provider);
}
public static void InitializeControl(
CustomCheckbox custom,
DocumentContainer container,
ErrorProvider provider)
{
custom.DocumentLoaded = true;
custom.Initialize(container);
}
public static void InitializeControl(
object _,
DocumentContainer container,
ErrorProvider provider)
{
// do nothing if the control is neither a
// ICustomControlWithText nor a CustomCheckbox
}
}
请注意,这只是为了支持以前版本的.NET 2.0,它没有动态支持,因为性能,动态更快,因为它缓存了调用方法所需的运行时解析,但在这种情况下,InvokeMember的解析需要更长时间。
替代方法是解决方法并将其缓存,如下所示。
private Dictionary<Type,MethodInfo> cache =
new Dictionary<Type,MethodInfo>();
public static void Initialize(Control control,
DocumentContainer container, ErrorProvider provider)
{
if (control == null) return;
MethodInfo initializer = null;
Type controlType = control.GetType();
if(!cache.TryGetValue(controlType, out initializer)){
initializer = typeof(ClassInitialor).GetMethod("InitializeControl",
new Type[] {
controlType,
typeof(DocumentContainer),
typeof(ErrorProvider),
});
cache[controlType] = initializer;
}
initializer.Invoke(null,
new object[] {
control,
container,
provider
});
foreach (Control subControl in control.Controls)
Initialize(subControl, container, provider);
}
答案 3 :(得分:0)
从第二个接口中删除初始化逻辑,只留下验证逻辑,所以你可以写这样的东西:
public interface ICustomControl
{
void Clear();
FieldType FieldType { get; set; }
bool DocumentLoaded { get; set; }
void Initialize(DocumentContainer container);
}
public interface ICustomControlWithText
{
string Text { get; set; }
ErrorProvider ErrorProvider { get; set; }
List<IValidation> Validations { get; set; }
void Validate(ErrorProvider provider);
}
class CustomCheckbox : CheckBox, ICustomControl { ... }
class CustomTextbox : TextBox, ICustomControl, ICustomControlWithText {...}
public static void Initialize(Control control, DocumentContainer container, ErrorProvider provider)
{
var custom = control as ICustomControl;
if (control == null)
return;
custom.DocumentLoaded = true;
custom.Initialize(container);
var controlWithTextBase = control as ICustomControlWithText;
if (controlWithTextBase != null)
controlWithTextBase.Validate(provider);
foreach (Control subControl in control.Controls)
Initialize(subControl, container, provider);
}
当然你可以制作ICustomControlWithText&#34;继承&#34;如果你想要ICustomControl
public interface ICustomControlWithText : ICustomControl
答案 4 :(得分:0)
也许基于多态的东西。我认为这很简单,很简单:
class TempTest
{
public static void Run()
{
IData data = new InitData() { IntegerData = 1, StringData = "some" };
IBaseControl c1 = new ControlA();
IBaseControl c2 = new ControlB();
c1.Init( data );
c2.Init( data );
}
}
// Interfaces
public interface IData
{
int IntegerData { get; set; }
string StringData { get; set; }
}
public interface IBaseControl
{
void Init( IData data );
}
public interface IControlA
{
void Init( int IntegerData );
}
public interface IControlB
{
void Init( int IntegerData, string StringData );
}
// Base classes
public abstract class Base : IBaseControl
{
#region IBaseControl Members
public abstract void Init( IData data );
#endregion
}
// Concrete classes
public class InitData : IData
{
public int IntegerData { get; set; }
public string StringData { get; set; }
}
public class ControlA : Base, IControlA
{
public override void Init( IData data )
{
Init( data.IntegerData );
}
#region IControlA Members
public void Init( int IntegerData )
{
Console.WriteLine( "ControlA initialized with IntegerData={0}", IntegerData );
}
#endregion
}
public class ControlB : Base, IControlB
{
public override void Init( IData data )
{
Init( data.IntegerData, data.StringData );
}
#region IControlB Members
public void Init( int IntegerData, string StringData )
{
Console.WriteLine( "ControlB initialized with IntegerData={0} and StringData={1}", IntegerData, StringData );
}
#endregion
}
我想你明白了。
答案 5 :(得分:0)
我会做以下事情:
将方法void Initialize(DocumentContainer container, ErrorProvider provider);
从ICustomControlWithText
移至ICustomControl
从
Initialize
方法签名
醇>
public void Initialize(DocumentContainer容器)
到
public void Initialize(DocumentContainer容器,ErrorProvider提供程序);
是的,CustomCheckBox永远不会使用provider
变量,但那是什么?在执行时间和代码大小方面,为方法调用添加一个额外的参数比使用访问者或动态方法(恕我直言)
以下列方式重写递归方法:
public static void Initialize(Control control, DocumentContainer container, ErrorProvider provider)
{
if (control == null)
{
return;
}
var custom = control as ICustomControl;
if (custom != null)
{
custom.DocumentLoaded = true;
custom.Initialize(container, provider);
}
foreach (Control subControl in control.Controls)
{
Initialize(subControl, container, provider);
}
}
答案 6 :(得分:0)
我一直使用这种模式:
首先,定义初始化器接口。该接口将用于定义控件类型的初始化方式。
public interface IInitializer
{
void Intialize(Control c, DocumentContainer container, ErrorProvider provider);
bool Accept(Control c);
}
其次,创建一些初始化器,每个初始化器代表一个if语句。
public class InitializerForControlWithTextBase : IInitializer
{
public void Intialize(Control control, DocumentContainer container, ErrorProvider provider)
{
var c = control as ICustomControlWithText;
c.DocumentLoaded = true;
c.Initialize(container, provider);
}
public bool Accept(Control c)
{
return GetBaseType().IsInstanceOfType(c);
}
public Type GetBaseType()
{
return typeof(ICustomControlWithText);
}
}
public class InitializerForCustomCheckbox : IInitializer
{
public void Intialize(Control control, DocumentContainer container, ErrorProvider provider)
{
var c = control as CustomCheckbox;
c.DocumentLoaded = true;
c.Initialize(container);
}
public bool Accept(Control c)
{
return GetBaseType().IsInstanceOfType(c);
}
public Type GetBaseType()
{
return typeof(CustomCheckbox);
}
}
第三,重写Initialize。
public static void Initialize(IEnumerable<IInitializer> initializers, Control control, DocumentContainer container, ErrorProvider provider)
{
if (control == null) return;
var inSituInitializer = control as IInitializer;
if (inSituInitializer != null)
{
inSituInitializer.Intialize(control, container, provider);
}
else
{
foreach (var initializer in initializers)
{
if (initializer.Accept(control))
{
initializer.Intialize(control, container, provider);
break;
}
}
}
foreach (Control subControl in control.Controls)
{
Initialize(initializers, subControl, container, provider);
}
}
你会注意到我首先检查控件是否已经实现了IIntializer。如果是,那么您可以直接进行初始化而无需进一步搜索(很容易将其放在您自己的代码上)。如果使用第三方控件,则可以找到并使用上面步骤2中的一个初始值设定项。
最后,在某个地方调用初始化程序。
var inits = new IInitializer[]
{
new InitializerForControlWithTextBase(),
new InitializerForCustomCheckbox(),
};
Initialize(inits, control, container, provider);
这种方法的好处是:
你有两种方法可以找到一个初始化器 - 把它放在你的控制之下,或者循环找到一个用于第三方控制的方法。 (如果你有数百个初始化器,你当然可以通过字典进一步提高循环性能。)
代码已解耦 - 您可以将各个初始值设定项放在单独的DLL中。