无法使用约束实现具有多个泛型参数的方法?

3

我有以下接口声明:

public interface IBasePresenter
{
    void Run();
    void ShowDialog<T, M>(T t, M m ) where T : UserControl where M : Form,      ISomeInterface<SomeType>;
}

ShowDialog()是一种方法,它可以向用户显示模态对话框。其中'T'是父窗体,而M是要显示的唯一对话框。这里有许多不同类型的M!因此选择一种通用的方法。

我认为这种方法可以有几种使用方式:

Presenter.ShowDialog(this, typeof(Form1)); // FigA

或者

Presenter.ShowDialog(this, new Form1()); // FigB

根据图A或B,一个示例ShowDialog()方法的实现看起来会是什么样子?

我的问题源于试图弄清楚在ShowDialog()方法实现中如何实例化泛型参数'M'。


你的问题并不是很清楚。你说了它“源自”什么,但你并没有真正提出一个问题...甚至在“源自”这个问题中也没有解释你的意思。 - Jon Skeet
@IbrarMumtaz:不,指定c#-4.0并不指定你正在使用哪个框架版本。你可能会针对.NET 2.0进行目标设置。 - Jon Skeet
@IbrarMumtaz:此外,C#有32K的关注者,而C#-4.0只有1.7K。你很可能会获得更多的浏览量。 - Tudor
4个回答

5
猜测情况如下:
m.Controls.Add(t);
m.ShowDialog();

不过,说实话我不确定这个实用方法是否真的有很多用处,它也可以是非泛型的(void ShowDialog(Control t, Form m))。如果使用: new()约束,则可能会更有用,这也可以避免在多个表单上使用相同的控件实例的风险(不合法)。但正如我所说:除非它已经证明了一些非平凡的有用性,否则我不会费心去使用这个方法。如果我要保留它,我会将参数重命名以使其更加清晰易懂;M、m、T、t都没有告诉我它们代表什么。


3
我同意关于泛型在这里没有太多意义的观点。 - Tudor
实际上在这里将其变得非通用化是有意义的,所以谢谢! - IbrarMumtaz
@IbrarMumtaz 顺便说一下,但是在这里使用FigB更有意义,除了它永远不会被处理。你可能需要在所有内容周围添加using(m) {...} - Marc Gravell
是的。除非必须针对派生控件或窗体执行特定操作,否则仅通过派生类型的赋值兼容性就可以解决问题,泛型完全是多余的。 - Olivier Jacot-Descombes

4
您不能使用Fig A的方式,因为typeof(Form1)是一个System.Type而不是Form;除非有一个接受第二个参数类型为System.Type的重载,否则代码将无法编译。

在ShowDialog()方法实现中如何实例化泛型参数'M'?

它不是“实例化”,而是“推断”。您已经提供了实例;编译器会从调用中“推断”类型。

2
您可以将通用方法签名更改为以下内容:
public void ShowDialog<T>() where T : Form, new() {
    using(var dialog = new T()){
        dialog.ShowDialog();
    }
}

然后就是调用:

ShowDialog<MyCoolDialog>();

这将导致方法创建一个新的表单实例,并以模态方式显示。


2
注意:如果这样做,将“using”添加到新表单中会是一个好主意,否则该表单将不会被处理。当然,这可能意味着删除控件“t”也是一个好主意。 - Marc Gravell
谢谢你,这对我来说是一个很好的指引。明天我会发布我的答案,展示一下现在的 'ShowDialog()' 是什么样子以及我如何使用它。希望能帮助其他处于类似情况的人们。 - IbrarMumtaz

0
以下是接口方法的略微更新版本:
    void ShowDialog<TParentForm, TDialogForm, TModel, TEntity>(TParentForm t, TDialogForm m, Action callback)
        where TParentForm : UserControl
        where TModel : class, IModel<TEntity>, new()
        where TDialogForm : Form, IEditableItem<TEntity>, new();

在之前的版本中,我做了一些假设,因此在测试和改进阶段中,方法签名已经发生了变化。对我来说,这仍然是一个教育性的练习,所以我仍然想知道如何完成它,而不是简单地选择捷径。

该方法的一个示例实现

    public void ShowDialog<TParentForm, TDialogForm, TModel, TEntity>(TParentForm t, TDialogForm m, Action callback)
        where TParentForm : UserControl
        where TModel : class, IModel<TEntity>, new()
        where TDialogForm : Form, IEditableItem<TEntity>, new()
    {
        using (var dialogToShow = new TDialogForm())
        {
            dialogToShow.StartPosition = FormStartPosition.CenterScreen;
            dialogToShow.FormBorderStyle = FormBorderStyle.FixedSingle;
            dialogToShow.Model = new TModel();

            // 2. show the new user control/form to the user.
            var result = dialogToShow.ShowDialog(t);

            // 3. handle the dialog result returned and update the UI appropriately.
            if (result == DialogResult.OK)
            {
                // print status label.
                callback.Invoke();
            }
        }
    }

我不太确定为什么 'TDialogForm m' 参数仍然存在,因为它似乎在任何地方都没有被使用。

如何使用该方法:

    private void BtnAddNewServiceClick(object sender, EventArgs e)
    {            
        Presenter.ShowDialog<ServerRolesControl, AddNewServiceForm, ServiceModel, Role>(this, new AddNewServiceForm(), SetAddedRolesLabel);
    }

    private void BtnViewAllServicesClick(object sender, EventArgs e)
    {
        Presenter.ShowDialog<ServerRolesControl, ViewRolesForm, ServiceModel, Role>(this, new ViewRolesForm(), SetDeletedRolesLabel);
    }

我应该更新接口方法,但是让它正常工作非常痛苦,现在我宁愿不去碰它 =)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接