c# – 当一个类型需要额外的属性时,尝试实现多态性

我有两个接口:

interface IDynamicControl
{
    string Id { get; set; }
    string Label { get; set; }
    string Value { get; set; }
}

interface IDynamicList : IDynamicControl
{
    IList ListItems { get; set; }
}

我有一个返回IDynamicControl的ControlResolver类 – 为了我的目的,我有ASP.NET TextBoxes,CheckBoxes和DropDownLists的包装类,它们实现IDynamicControl(DropDown实现IDynamicList).要点是我可以给解析器一个控件的名称,例如“Textbox”,它会给我一个IDynamicControl,它是一个修改过的ASP.NET文本框.

这很有效,但问题是DropDownList.也许我有一个大脑放屁,但我遇到的问题是,当控件是下拉列表时,我必须对IDynamicList进行显式转换(因为解析器返回IDynamicControl)所以我可以添加项目到它用于显示.这通常不是问题,但动态控制的目的是我可以在外部存储字段类型并将其读入,因此我必须做一些丑陋的事情:

string controlType = SomeService.GetControlType();
if (controlType == "dropdown")
{
    var control = (IDynamicList)ControlResolver.ResolveControl(controlType);
    // set up list items
}
else
{
    var control = ControlResolver.ResolveControl(controlType);
    // stuff with normal controls
}

但这看起来相当丑陋.我可以在基类中包含ListItems属性,只是在不使用它的类中抛出NotImplemented,但这违反了ISP,甚至比if语句更有气味.总之,我想让我的解析器返回一种控件,所以我不需要使用不同的Resolve()方法,但是在使用代码时,如果只有控件那么我需要做额外的工作是下拉列表.

除非我弄错了或者我忘记了一些基本的东西,有没有更好的解决方案来做这个或我应该只使用if语句?我不能使用基类,因为我的所有“动态”类都继承自基本ASP.NET UI类.

最佳答案 我认为将原始代码重构为更简洁的最简单方法如下:

        var control = ControlResolver.ResolveControl(controlType);

        // ... do common logic for all controls

        var list = control as IDynamicList;
        if (list != null)
        {
            // .. do additional logic for list controls
        }

您调用ControlResolver.ResolveControl的代码实际上不应该知道“下拉”控件的任何特殊规则.它真正需要知道的是解析的控件是否实现了IDynamicList接口.

当然,您仍然需要一些条件逻辑,但至少条件是基于您的代码已经知道的众所周知的接口而不是底层的实现细节.

当然,您可以采取其他方法来进一步沿着抽象的路径前进,但这似乎是一个小的变化,可以在不影响任何其他代码的情况下进行.

点赞