代表作为属性:坏主意?

发布于 2024-12-06 23:57:24 字数 2346 浏览 0 评论 0原文

考虑以下控件(为了简洁而进行了剪裁):

public partial class ConfigurationManagerControl : UserControl
{

    public Func<string, bool> CanEdit { get; set;}
    public Func<string, bool> CanDelete { get; set; }

    public Dictionary<string, string> Settings
    {
        get { return InnerSettings; }
        set
        {
            InnerSettings = value;
            BindData();
        }
    }
    private Dictionary<string, string> InnerSettings;

    private void OnListIndexChanged(object sender, EventArgs e)
    {
        this.EditButton.Enabled = false;
        this.DeleteButton.Enabled = false;

        var indices = this.List.SelectedIndices;
        if (indices.Count != 1)
        {
            return;
        }

        var index = indices[0];
        var item = this.List.Items[index];

        if (this.CanEdit != null)
        {
            this.EditButton.Enabled = this.CanEdit(item.Text);
        }

        if (this.CanDelete != null)
        {
            this.DeleteButton.Enabled = this.CanDelete(item.Text);
        }

    }
}

该控件还有更多内容,但足以说明它允许用户添加、编辑和删除 Dictionary中的条目。为了确定是否允许用户编辑或删除条目,它使用委托方法properties、CanDelete和CanEdit,它们由表单提供或托管它的控件:

public class SetupWizard : Form
{
    public SetupWizard()
    {
        InitializeComponent();

        this.SettingManager.CanEdit = CanEditSetting;
        this.SettingManager.CanDelete = CanDeleteSetting;
    }

    private static bool CanEditSetting(string item)
    {
        var lockedSettings = new[] { "LicenseHash", "ProductHash" };
        return !lockedSettings.Contains(item.ToLower());
    }

    private static bool CanDeleteSetting(string item)
    {
        var lockedSettings = new[] {
                                        "LicenseHash",
                                        "ProductHash", 
                                        "UserName", 
                                        "CompanyName"
                                    };
        return !lockedSettings.Contains(item.ToLower());
    }
}

我发现这种设计既令人满意又令人担忧。一方面,它似乎使用最简单的可行解决方案来解决问题(它确实很好地分离了关注点)。另一方面,我一直担心我不正确地使用委托,而应该使用事件(即使我不需要多个侦听器,并且只需要调用者告诉我是否该项目是可编辑的)。

另一方面,有可能存在一种完全不同的设计,我什至没有考虑过它可能以一种非常优越的方式解决问题。

所以。这个设计在技术上是否正确、可维护且灵活?或者我应该做一些更好的事情?

Consider the following control (snipped for brevity):

public partial class ConfigurationManagerControl : UserControl
{

    public Func<string, bool> CanEdit { get; set;}
    public Func<string, bool> CanDelete { get; set; }

    public Dictionary<string, string> Settings
    {
        get { return InnerSettings; }
        set
        {
            InnerSettings = value;
            BindData();
        }
    }
    private Dictionary<string, string> InnerSettings;

    private void OnListIndexChanged(object sender, EventArgs e)
    {
        this.EditButton.Enabled = false;
        this.DeleteButton.Enabled = false;

        var indices = this.List.SelectedIndices;
        if (indices.Count != 1)
        {
            return;
        }

        var index = indices[0];
        var item = this.List.Items[index];

        if (this.CanEdit != null)
        {
            this.EditButton.Enabled = this.CanEdit(item.Text);
        }

        if (this.CanDelete != null)
        {
            this.DeleteButton.Enabled = this.CanDelete(item.Text);
        }

    }
}

There's more to this control, but suffice it to say that it allows a user to add, edit, and delete the entries in a Dictionary<string, string>. In order to determine whether or not it should allow the user to edit or delete the entries, it uses the delegate method properties, CanDelete and CanEdit, which are provided by the form or control that hosts it:

public class SetupWizard : Form
{
    public SetupWizard()
    {
        InitializeComponent();

        this.SettingManager.CanEdit = CanEditSetting;
        this.SettingManager.CanDelete = CanDeleteSetting;
    }

    private static bool CanEditSetting(string item)
    {
        var lockedSettings = new[] { "LicenseHash", "ProductHash" };
        return !lockedSettings.Contains(item.ToLower());
    }

    private static bool CanDeleteSetting(string item)
    {
        var lockedSettings = new[] {
                                        "LicenseHash",
                                        "ProductHash", 
                                        "UserName", 
                                        "CompanyName"
                                    };
        return !lockedSettings.Contains(item.ToLower());
    }
}

I find that this design is both satisfactory and worrisome at the same time. On the one hand, it seems to solve the problem using the simplest solution that works (it certainly separates the concerns nicely). On the other hand, I have this nagging concern that I am using delegates improperly and should be using an event, instead (even though I do not need multiple listeners, and only need the caller to tell me if the item is editable).

And then, on the other other hand, there's the chance that there's a completely different design that I haven't even considered that might solve the problem in a vastly superior way.

So. Is this design technically correct, maintainable, and flexible? Or should I be doing something better?

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

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

发布评论

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

评论(2

回梦 2024-12-13 23:57:24

我建议使用带有这两种方法的接口。这更清晰:

interface ICantThinkOfAGoodName
{
    bool CanEdit(string item);
    bool CanDelete(string item);
}

您可以创建类似于许多 MVVM 框架中使用的 RelayCommand 的东西:

public class RelayObject : ICantThinkOfAGoodName
{
    public RelayObject() : this(null, null) {}
    public RelayObject(Func<string, bool> canEdit, Func<string, bool> canDelete)
    {
        if(canEdit == null) canEdit = s => true;
        if(canDelete == null) canDelete = s => true;

        _canEdit = canEdit;
        _canDelete = canDelete;
    }

    public bool CanEdit(string item)
    {
        return _canEdit(item);
    }
    public bool CanDelete(string item)
    {
        return _canDelete(item);
    }
}

像这样使用它:

public SetupWizard()
{
    InitializeComponent();

    this.SettingManager.PropertyName = new RelayObject(CanEditSetting, 
                                                       CanDeleteSetting);
    // or (all can be deleted)
    this.SettingManager.PropertyName = new RelayObject(CanEditSetting, null);
    // or (all can be edited)
    this.SettingManager.PropertyName = new RelayObject(null, CanDeleteSetting);
    // or (all can be edited and deleted)
    this.SettingManager.PropertyName = new RelayObject();

}

顺便说一句:我在这里使用属性注入,因为它是一个控件。通常,我会在 ConfigurationManagerControl 的构造函数中传递 ICantThinkOfAGoodName 依赖项。

I suggest the use of an interface with these two methods. That's a lot cleaner:

interface ICantThinkOfAGoodName
{
    bool CanEdit(string item);
    bool CanDelete(string item);
}

You could create something similar to the RelayCommand used in many MVVM frameworks:

public class RelayObject : ICantThinkOfAGoodName
{
    public RelayObject() : this(null, null) {}
    public RelayObject(Func<string, bool> canEdit, Func<string, bool> canDelete)
    {
        if(canEdit == null) canEdit = s => true;
        if(canDelete == null) canDelete = s => true;

        _canEdit = canEdit;
        _canDelete = canDelete;
    }

    public bool CanEdit(string item)
    {
        return _canEdit(item);
    }
    public bool CanDelete(string item)
    {
        return _canDelete(item);
    }
}

Use it like this:

public SetupWizard()
{
    InitializeComponent();

    this.SettingManager.PropertyName = new RelayObject(CanEditSetting, 
                                                       CanDeleteSetting);
    // or (all can be deleted)
    this.SettingManager.PropertyName = new RelayObject(CanEditSetting, null);
    // or (all can be edited)
    this.SettingManager.PropertyName = new RelayObject(null, CanDeleteSetting);
    // or (all can be edited and deleted)
    this.SettingManager.PropertyName = new RelayObject();

}

BTW: I am using Property injection here, because it is a control. Normally, I would pass the ICantThinkOfAGoodName dependency in the constructor of the ConfigurationManagerControl.

生活了然无味 2024-12-13 23:57:24

这可能就是 @Daniel Hilgarth 当他说“使用接口”时所建议的(注意 - 他的答案现在反映了实现接口的更通用/灵活的方法)。与其直接将委托分配给您的方法,为什么不为控件提供一个属性,例如 DataState 或任何您想要调用它的属性,使用封装您需要的信息的接口,然后将其留给所有者决定如何实施。

interface IDataState
{
    bool CanEdit(string item);
    bool CanDelete(string item);
}

public partial class ConfigurationManagerControl : UserControl
{
    public IDataState DataState {get;set;}
    // your code checks DataState.CanEdit & DataState.CanDelete
}

public class SetupWizard : Form, IDataState
{
    public SetupWizard()
    {
        InitializeComponent();
        SettingManager.DataState =this;
    }
    public bool CanEdit(string item)
    { 
        ... implement directly or return from your private function
    }
    public bool CanDelete(string item)
    { 

    }
}

但这使您可以灵活地以任何您选择的方式(使用另一个对象等)实现该接口,并且还可以轻松地传递所有者本身(实现接口)。

It may be this is what @Daniel Hilgarth is suggesting when he says "use an interface" (n.b. - his answer now reflects a more general/flexible approach to implementing the interface). Instead of assigning delegates to your method directly, why not give the control a property, such as DataState or whatever you want to call it, using an interface that encapsulates the information you need, and leave it up to the owner to decide how to implement that.

interface IDataState
{
    bool CanEdit(string item);
    bool CanDelete(string item);
}

public partial class ConfigurationManagerControl : UserControl
{
    public IDataState DataState {get;set;}
    // your code checks DataState.CanEdit & DataState.CanDelete
}

public class SetupWizard : Form, IDataState
{
    public SetupWizard()
    {
        InitializeComponent();
        SettingManager.DataState =this;
    }
    public bool CanEdit(string item)
    { 
        ... implement directly or return from your private function
    }
    public bool CanDelete(string item)
    { 

    }
}

But this gives you the flexibility to implement that interface any way you choose, with another object, etc. and it makes it easy to also just pass the owner itself (implementing the interface).

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