【问题标题】:Passing a user defined method to the constructor将用户定义的方法传递给构造函数
【发布时间】:2016-04-25 21:04:19
【问题描述】:

我想创建一个简单的输入对话框。所以我在我的应用程序中添加了一个 Windows From 并在该窗口中放置了一个标签、一个文本框和一个按钮;现在很容易使用构造函数用预定义的值填充我的控件所需的属性,并用result返回输入的值;

public partial class GeneralInputForm : Form
{
    public string result = "";
    public GeneralInputForm(string label, string defaultValue = "")
    {
        InitializeComponent();
        label1.Text = label;
        textBox1.Text = defaultValue;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        result = textBox1.Text;
        Close();
    }
}

现在创建一个 from 的实例并用所需的值填充参数;

GeneralInputForm formInput = new GeneralInputForm("Please enter your age:");
formInput.ShowDialog();

//formInput.result      holds the value

我的问题在这里提出;如果我想检查用户在 GeneralInputForm 本身的文本框中输入的值的有效性(而不是稍后通过检查 result 字段来实例化表单时)怎么办?我的意思是 C# 中是否有这样的功能(可能是委托),以便用户自己可以定义一个方法并将其作为构造函数的参数之一传递,该方法检查 textbox1.text 值以确保例如它是成功可以解析成int or float or string?

我不想在我的构造函数中添加一个名为variableType 的丑陋字符串参数,并要求用户说出他是否需要“int”或“float”或“string”或其他什么,然后我自己通过检查variableType 的值,编写了不同的语句以确保输入的文本可以成功解析为 int 或 float 或 string(这将是一个丑陋且有限的解决方案)

【问题讨论】:

  • 既然您已经考虑过委托,那您为什么不最终使用它们?
  • 我不明白您是否希望被调用的表单执行任何转换。似乎您不要求此作业由被调用的表单执行,但您想要一个 GenericInputForm 回调以检查输入有效性的委托。我错了吗?
  • 您得到了答案,但您没有提供反馈。
  • 问题是如果使用委托就是答案,我不知道如何实现它

标签: c# methods delegates


【解决方案1】:

GeneralInputForm 更改为GeneralInputForm<T>。是一个泛型类,就像List<String>

您可以使用两个字段 Func 一个将 Text 转换为您想要的对象,一个检查返回,就像这样:

public partial class GeneralInputForm<T> : Form
{
    public T result = default(T);
    Func<T, Boolean> check = (input) => true; // Default check
    Func<String, T> cast = null; // Default cast

    public GeneralInputForm(string label, string defaultValue = "", Func<String, T> cast, Func<T, Boolean> check)
    {
        InitializeComponent();
        label1.Text = label;
        textBox1.Text = defaultValue;
        if(check != null)
            this.check = check;
        this.cast = cast;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        if(cast != null)
        {
            T casted = cast(textBox1.Text);

            if(casted != null && check(casted)){
                result = textBox1.Text;
                Close();
            }
        }
    }
}

你可以这样使用它:

Func<String, int> cast = (input) => int.Parse(input);
Func<int, Boolean> check = (input) => input > 0;
GeneralInputForm<int> form = new GeneralInputForm<int>("Enter a number:", "1", cast, check);
form.ShowDialog();
//etc....

More info about Generics.

这种做事方式叫做Dependecy Injection,这种方式就是构造函数注入类型。 More info.

【讨论】:

  • 对于这一行:public T result = "";我收到此错误:无法将类型字符串隐式转换为 T
  • 你说得对,你知道的复制粘贴错误...更新了。
  • 虽然这个想法很好(而且可行),但除非绝对必要,否则我会避免为表单使用泛型类型,特别是如果您想从它们继承时
  • 所以我们只需要从 ecuation 中移除演员表并在检查中进行演员表。但是强制转换和依赖注入正是为了避免继承。
【解决方案2】:

您不需要将其作为参数传递。可以使用继承覆盖验证函数。

  public partial class GeneralInputForm : Form
  {
      // Returns true if this text is valid for a textbox
      protected virtual bool IsValid(string text)
      {
          return true;                    // default behaviour
      }

      private void button1_Click(object sender, EventArgs e)
      {
          if (IsValid(textBox1.Text))
          {
              result = textBox1.Text;     // ok
              Close();
          }
          else
          {
              // Show error message
          }
      }
  }

现在您可以通过覆盖(即替换) IsValid() 函数来创建自定义验证文本框的表单:

  public partial class SpecificInputForm : GeneralInputForm
  {
       // Blank textboxes are not valid, and are restricted to 31 characters
       protected override bool IsValid(string text)
       {
           if (string.IsNullOrWhitespace(text))
               return false;

           return (text.Length < 32);
       }
  }

【讨论】:

    【解决方案3】:

    虽然@Oscar 的回答会奏效,但将表单设为通用是一个坏主意,因为 Winforms 设计者厌恶它们(尝试从该表单继承,你会看到)。

    我可能会让它不是通用的形式,而是一种方法:

    public partial class GeneralInputForm : Form
    {
      private GeneralInputForm() // Private constructor
      {
        InitializeComponent();
      }
    
      public static bool TryGetValue<T>(
                 string label, 
                 string defaultValue, 
                 Func<T, bool> check, 
                 out T result
             ) where T : IConvertible
      {
        result = default(T);
        using (var frm = new GeneralInputForm())
        {
          frm.label1.Text = label;
          frm.textBox1.Text = defaultValue;
          frm.Closing += (o, e) =>
          {
            var myForm = (GeneralInputForm) o;
            // User closed not clicking the button?
            if (myForm.DialogResult != DialogResult.OK) 
              return;
            try
            {
              var checkval = (T) Convert.ChangeType(myForm.textBox1.Text, typeof (T));
              if (!check(checkval))
                e.Cancel = true;
            }
            catch
            {
              e.Cancel = true;
            }
    
          };
          if (frm.ShowDialog() == DialogResult.OK)
          {
            result = (T)Convert.ChangeType(frm.textBox1.Text, typeof (T));
            return true;
          }
        }
        return false;
      }
    
      private void button1_Click(object sender, EventArgs e)
      {
        // Don't use `Close()` on modal forms, it's always better to 
        // return a dialog result. This will close the form aswell
        DialogResult = DialogResult.OK; 
    
      }
    }
    

    然后像这样使用它:

    int result;
    if(GeneralInputForm.TryGetValue("Enter an integer over 10", 
                                    "10", 
                                    (x) => x > 10, 
                                    out result))
      MessageBox.Show(result.ToString());
    else
      /* User closed the form by other means */
    

    或者:

    string result;
    if (GeneralInputForm.TryGetValue("Enter \"Hello\"",
                                     "Goodbye", 
                                     (x) => x == "Hello", 
                                     out result))
    

    这不会让您通过单击button1 来关闭表单,除非该值正确,但会让您通过其他方式关闭它(alt+f4、单击x 等)。如果它被按钮关闭,它会返回true,如果不是,它会返回false(你可以在那里放一个Cancel按钮)。

    您还可以将defaultValue 参数设为T 类型并将defaultValue.ToString() 分配给文本框

    如果值无效,可以在Closing 委托中向用户显示消息框,或者您可以自己制定逻辑

    这要求TIConvertible,但是任何具有Parse 方法的类型都是

    【讨论】:

    • 虽然设计器不会使用通用表单的派生类,但依赖注入方式允许您避免继承,因为您可以为表单提供所需的所有功能。
    【解决方案4】:

    感谢大家的回答;我最终使用委托得到了这个简单明了的解决方案;

    public partial class SimpleInputForm : Form
    {
        public string Value = "";
        public delegate bool VerifyDel(string input);
        private VerifyDel Methods;
        public SimpleInputForm(string lable, VerifyDel method)
        {
            InitializeComponent();
            label1.Text = lable;
            Methods = new VerifyDel(method);
        }
        private void button1_Click(object sender, EventArgs e)
        {
            if (Methods(textBox1.Text))
            {
                Value = textBox1.Text;
                Close();
            }
        }
    }
    

    现在我展示了带有用户定义功能的表单,用于评估输入;

    SimpleInputForm simpleInput = new SimpleInputForm("Enter your age:", (input => {
        try
        {
            int age = int.Parse(input);
            return true;
        }
        catch
        {
            MessageBox.Show("Invalid value for age!");
            return false;
        }
    }));
    
    //simpleInput.Value     holds the input value
    

    【讨论】:

      猜你喜欢
      • 2011-11-27
      • 1970-01-01
      • 2014-09-10
      • 2019-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-21
      相关资源
      最近更新 更多