【问题标题】:Is it OK to overload ShowDialog() so that a child form returns information as an out parameter?是否可以重载 ShowDialog() 以便子表单将信息作为输出参数返回?
【发布时间】:2008-11-11 15:45:12
【问题描述】:

在之前关于 how to return a string from a dialog window 的问题中,yapiskan 建议 overloading the child form's ShowDialog() method 包含一个 out 参数。

我的问题是这是否是 C# 中的好方法。

这里是一些示例代码,基于 yapiskan 的建议。在子窗体中(在本例中,它是一个带有文本框的窗体),您只需添加 ShowDialog 重载并将 UI 值分配给 out 参数:

public DialogResult ShowDialog(out string s)
{
    DialogResult result = this.ShowDialog();
    s = this.textBox1.Text;
    return result;
}

要显示表单并检索输入的文本,请执行以下操作:

using (CustomDialog frm = new CustomDialog())
{
    string s;
    if (frm.ShowDialog(out s) == DialogResult.OK)
    {
        // do something with s
    }
}

我能想到的一个优点是,这种方法迫使 CustomDialog 表单的用户通过表单的 ShowDialog 方法(而不是像 GetMyData()什么的)。

【问题讨论】:

    标签: c# .net winforms


    【解决方案1】:

    最好有一个公共属性/方法并获取信息。

    如果你需要 3..4..5 条信息,有 5 个参数,你会怎么做?让访问者从对话框中获取您的信息更加干净。

    【讨论】:

    • 我不确定。使用对话框上的参数,您无法确保用户知道可用的信息(例如,他可能会错过 GetFullName() 方法并改为编写自己的方法)。一个 ShowDialog 重载中的所有内容,他不能错过它。
    • 澄清:他可能会通过组合对话框的 GetFirstName 和 GetLastName 方法来编写自己的。
    • 如果他需要,他会让访问者获取信息。他可能并不总是需要信息。在这种情况下,他会很好地使用访问器。否则,如果没有 out,他可能不需要字符串,但必须创建一个变量......什么都没有......
    • 从一个方法(ShowDialog 重载或 GetData 或其他方法)获取所有信息的一个优点是,如果添加参数,则必须修改任何调用类以了解它,以便编译。
    • 另一点是属性的使用是这里的既定模式,并且运行良好 - 尤其是因为(如上所述)如果用户取消,可能没有“out”可以返回。
    【解决方案2】:

    应该不行,因为.net框架没有使用这种设计。对于 OpenFileDialog 类,它有一个返回 DialogResult 的无参数 ShowDialog() 方法。调用此方法后,用户应该使用 FileName、FileNames、SafeFileName 和 SafeFileNames 方法获取选定的文件。

    让我们假设这是以“输出参数”的方式实现的。我必须编写这样的代码才能获得 SafeFileName:

    string dummyFileName;
    string[] dummyFileNames;
    string safeFileName;
    string[] dummySafeFileNames;
    
    myDialog.ShowDialog(out dummyFileName, out dummyFileNames, out safeFileName, out dummySafeFileNames);
    

    【讨论】:

    • 我无法反驳。这看起来确实很糟糕。
    【解决方案3】:

    就我个人而言,我尽量避免使用 out 参数,尽管我知道像 GoTo 一样,它们有时是必要的邪恶。我会说使用属性或方法来返回信息会更好。

    【讨论】:

      【解决方案4】:

      根据我的经验,只收集一条信息的自定义模式对话框是一个非常极端的异常值。更常见的是零和多。

      收集许多数据的对话框几乎肯定会在某个时候被修改为只收集一个。我宁愿只修复使用那条新数据的代码,也不愿修复使用修改后对话框的每一段代码。

      此外,请考虑开发人员如何使用 IntelliSense 来使用您的类。他要输入这个:

      MyDialog d = new MyDialog();
      d.ShowDialog(
      

      ...在最后一次击键时,IntelliSense 会弹出,告诉他他现在必须声明三个新的字符串变量来保存输出参数。于是他向上移动光标,开始输入:

      string foo;
      string
      

      ...而且,第二个参数的名称又是什么?所以它回到了open paren,按CTRL+SPACE,哦,是的,它是bar,回到上一行,等等。

      在自定义对话框上使用属性的问题在于,Form 类已经拥有一百万个属性,而您正在创建的三四个特殊属性将会在混合中丢失。要解决此问题,请为对话框参数创建一个类,并在自定义对话框上创建该类型的 Parameters 属性。这使得这样的代码很容易编写:

      MyDialog d = new MyDialog();
      d.Parameters.Foo = "foo";
      d.Parameters.Bar = "bar";
      d.Parameters.Baz = "baz";
      

      因为在 IntelliSense 中会弹出参数名称,您不需要声明任何变量来保存它们的值。

      【讨论】:

      • 所有优点,谢谢。我喜欢 C#(我从 VB3 开始)和 OOP 的原因之一是我可以改变一些东西,然后依靠语言和 IDE 来迫使我在整个程序中处理这种改变。多属性方法没有给我。
      • Intellisense 以及在调用 ShowDialog 之前必须声明一个或多个变量是一个足够大的问题,我可能不会在工作中这样做。我希望我的问题的答案之一是内联声明参数的某种方式。
      • 有一秒钟,我想我可以在我的示例中返回一个自定义参数对象而不是字符串,这样我只需在调用 ShowDialog 之前声明一个变量。但是调用者会很高兴地忽略任何新添加的字段。
      • 所以我想每个参数的声明是不可避免的,因为我声称这种方法的优势。
      【解决方案5】:

      我的方法通常是编写一个内部调用 ShowDialog 的方法,然后适当地格式化输出数据。对于(人为的)示例:

      public string GetFolderName(){
          if(this.ShowDialog() == DialogResult.OK) {
              return this.FolderName.Text;
          }
          return String.Empty;
      }
      

      在大多数情况下,我将此方法设为静态,并从方法主体内实例化对话框本身——这样调用者就不必处理表单引用,或者不必选择哪个“显示”的概念调用方法。

      在具有多个输出值的非边缘情况下,我通常构造一个包含这些值的结构,然后让我的“获取”函数返回该结构。

      public struct FolderData {
          public static FolderData Empty = new FolderData();
      
          public string FolderName {get; set;}
          public int FilesInFolder {get; set;}
      }
      
      public FolderData GetFolderData(){
          if(this.ShowDialog() == DialogResult.OK) {
              return new FolderData {
                  FolderName = this.FolderName.Text;
                  FilesInFolder = int.Parse(this.FilesInFolder.Text);
              }
          }
          return FolderData.Empty;
      }
      

      【讨论】:

        【解决方案6】:

        我更喜欢这个,因为我不喜欢在完成类之后从属性或方法获取结果的方法。在显示并关闭对话框表单后,我认为该对象不应再使用,因为从逻辑上讲您已经完成了对话框,那么我为什么要使用它的属性或方法来获取结果?

        【讨论】:

        • 好吧,如果你看到 using(...) 没有 out 或属性,它仍然在 if 之后的内存中,所以你可以使用它。所以你的最后一句话很好,但不是问题的例子。
        • 内存可能不是问题。我在这个和我认为的逻辑中;要么在内存里,不被使用就好了。
        • 你在逻辑上还没有完成对话框。它只是不再显示在屏幕上。
        • 你应该得到检查才能开始这一切。 :)
        • 正如另一个 Robert 所指出的,对话框并没有消失,只是被隐藏了。在这种情况下,我不会将使用 out 参数称为“最佳实践”,因为没有其他人提倡或认为这是个好主意。
        【解决方案7】:

        @Musigenesis,您真的不想在更改对话框时强制客户端代码中断,并且使用仅有时有效的 out 参数不是一个好的设计。正如@Daok 所说,当您返回超过 1 个值时,这开始变得混乱和难看。

        您也不能强制客户端代码使用结果,就像 .net 框架确保您调用文件对话框上的属性一样。你也没有强迫调用者对 out 参数做任何事情,你强迫他们做的只是接受一个他们可能不想使用的变量。

        如果对话框非常通用,这可能不适用,但与其将各种属性添加到对话框本身,不如使用一个在整个应用程序中一致使用的方法,并返回一个特定的类,该类包含相关数据。

        public sealed class MySaveDialogResult
        {
            public static MySaveDialogResult NonOkResult(); // Null Object pattern
            public MySaveDialogResult( string filePath ) { ... }
        
            // encapsulate the dialog result
            public DialogResult DialogResult { get; private set; } 
            // some property that was set in the dialog
            public string FilePath { get; private set; }
            // another property set in the dialog
            public bool AllowOVerwrite { get; private set; }
        }
        

        你的对话是

        public MySaveDialog ...
        {
            public MySaveDialogResult GetDialogResult() { .... }
        }
        

        本质是一个小的不可变实用程序类,它也实现了空对象模式。只要对话结果不正确,就会返回 null 对象。显然,上面的内容是为了满足您的需求而在黑暗中拍摄的,因此可以随意更改它,制作继承层次结构等。

        重点是让GetDialogResult(),对话框上的单个方法,返回一个封装所有相关对话框数据的类。


        编辑:

        @yapiskan 想知道为什么不直接“退出”MyDialogResult 而不是调用GetDialogResult()

        IMO - 要点很简单:

        1. 这不是惯例
        2. 方法调用非常简单,当您遵循上述“约定”参数时,方法调用会变得更容易。
        3. out 使用起来很尴尬。 GetDialogResult() 不会强制调用者编写笨拙的代码,也不会强制用户在调用对话框时消费对话框结果。
        4. 通常对话框不会重新实例化或重新显示以获得结果,它已经存在。 Show() 和 Hide() 就是这样做的。

        现实情况是,您正在用一个笨拙的 ShowDialog() 语法调用方法。方法调用很便宜,你不能保证调用者会使用你的 out 参数,就像你不能保证他们会调用 GetDialogResult() 一样。那何必呢。让这个东西易于使用,或者一开始就不要重载 ShowDialog。

        也许你的子类是时髦的并且行为不同,它不适用于你的情况,但一般的设计是当你点击 OK 时表单不会消失,当它们被 Disposed() 时它们会消失。

        【讨论】:

        • 所有优点,谢谢。我完全同意最后一点,我想要一个返回所有相关数据的对话框上的单一方法。我主要是独自工作,通常我会在对话框中添加一个 GetData() 方法或其他任何方法,然后毫无问题地使用它。 {更多...}
        • 但是最近在一个小组工作,我越来越意识到向类和表单添加其他开发人员从未意识到的东西的问题。将 GetData 方法与 ShowDialog 重载合并的想法很吸引我,因为其他开发人员必须与...进行交互...
        • ... 这样的对话框(因为没有其他方法可以获取其内部数据)。我不知道。当其他人都讨厌一个想法时,很难对它保持兴奋。 :)
        • 好吧,老实说,强迫代码破坏只会惹恼人们。最好发邮件之类的,只能鼓励人。至少对于获取所有相关对话数据的单一数据收集方法,他们丢失森林树木的可能性较小。
        • 成语是创建约定并遵循它们。这与 Spolsky 的“让错误的代码看起来错误”一脉相承。当您遵循约定时,您可以更轻松地针对它们进行编程。这不会一下子发生,而是在代码审查和讨论中发生。
        猜你喜欢
        • 2011-04-15
        • 2011-01-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-03-03
        • 1970-01-01
        相关资源
        最近更新 更多