将用户定义的方法传递给构造函数

时间:2016-04-25 21:04:20

标签: c# methods delegates

我想创建一个简单的输入对话框。所以我在我的应用程序中添加了一个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值以确保例如,它是否可以成功解析为intfloatstring

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

4 个答案:

答案 0 :(得分:1)

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

您可以使用两个字段Func one将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.

答案 1 :(得分:0)

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

  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);
       }
  }

答案 2 :(得分:0)

虽然@ 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方法的类型都是

答案 3 :(得分:0)

感谢大家的答案;我最终使用了代表这个简单明了的解决方案;

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