简体   繁体   中英

How do I call an instance method from another classes' static method (i.e. have only one object)?

my form1 class contains a bunch of data that I need to save, so there should only be one instance of it running at a time.

public partial class Form1 : Form
{
    public string form1string = "I really need to save this data";

    public Form1()
    {
        InitializeComponent();


        // Even if I pass my form1 object here I still can't access it from
        // the upcoming static methods.
        InterceptKeys hook = new InterceptKeys();
    }

InterceptKeys, which is not my code, contains a bunch of static methods required for keyboard hooks.

private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
    if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
    {
       int trueKeyPressed = Marshal.ReadInt32(lParam);

       if (Form1.mirror)
       {
           Form1.newKeyPress(trueKeyPressed);
           return (IntPtr)1;
       }
    }
    return CallNextHookEx(_hookID, nCode, wParam, lParam);
 }

Because the HookCallBack method is static, Form1.newKeyPress() needs to be static as well.

But if newKeyPress in static, I can't access the data that I need! I wouldn't want to declare a new form1 object here, as that would give me different versions of the data. Correct?

I'm not an Object Oriented Expert. How should I format this, to ensure that all of the InterceptKey's form1 method calls are going to THE form1 object that I want?

Thanks, please let me know if there's any more info that you need!

You have two design issues:

How to call an instance method from a static method

Because the HookCallBack method is static, Form1.newKeyPress() needs to be static as well.

You can pass in an instance of your main form to your HookCallBack method, you'd just need to add an additional parameter to your static method:

private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam, Form1 form)
{
   // ...
}

This is actually the preferred strategy. Wherever your methods and classes have dependencies on other objects, you should pass your dependencies into your methods instead of pulling them from global state.

Barring that, you can loop through the Application.OpenForms collection and find the form you're looking for as follows:

var form = Application.OpenForms.OfType<Form1>().First();
form.newKeyPress();

How to have one instance of a form open at one time

Other people have suggested making your form static -- that's one approach, but its a bad approach. Static forms don't get garbage collected when their disposed, you have to implement your own init/reset methods when you show/hide the form, if the static form has references to other objects then your app will slowly leak memory, among other things . I actually recommend something like this:

class FormFactory
{
    public Form1 GetForm1()
    {
        return Application.OpenForms.OfType<Form1>().FirstOrDefault ?? new Form1();
    }
}

So your FormFactory controls the lifetime of your form, now you can get an existing or new instance of Form1 using new FormFactory.GetForm1() .

Passing keypresses to other forms

It occurs to me that you're basically just passing keypresses to your form, which implies a kind of basic message notification / observer pattern. Maybe you just need a better design pattern. Try the following:

public class MessageHooks
{
    public static event Action<int> OnHookCallback;

    private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
        {
            int trueKeyPressed = Marshal.ReadInt32(lParam);
            if (OnHookCallBack != null)
            {
                OnHookCallback(trueKeyPressed);
            }
        }
        return CallNextHookEx(_hookID, nCode, wParam, lParam);
    }
}

Guess what? Now your HookCallBack method doesn't even need to know of the existence of forms. Instead, your forms register themselves to the event handler as such:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        MessageHooks.OnHookCallBack += MessageHooks_OnHookCallBack;
        this.FormClosed += (sender, e) => MessageHooks.OnHookCallBack -= MessageHooks_OnHookCallBack; // <--- ALWAYS UNHOOK ON FORM CLOSE
    }

    void MessageHooks_OnHookCallBack(int keyPressed)
    {
        // do something with the keypress
    }
}

Remember, you must unhook your forms event handler on form close, otherwise the form won't get garbage collected, and you'll run into lots of weird issues raising events on non-visible forms.

From what you've written, I think this will work:

In Form1 , have a static member of type Form1 which will hold the instance :

private static Form1 instance;

In the Form1 constructor, set this static member to the instance being created:

public Form1()
{
    // Existing code

    Form1.instance = this;
}

Now, make newKeyPress static, and use the static member to find the actual form instance:

public static void newKeyPress(int trueKeyPressed)
{
    Form1 thisIsTheForm1Instance = Form1.instance;

    // Now instance.form1string is the data you want
}

I think something like having in the Form1 class:

private static Form1 instance;


 public static Form1 Instance
 {
      get
      {
          if (instance == null)
          {
              instance = new Form1();
          }
          return instance;
      }
 }

And in your classes which use them:

Form1 form1 = Form1.Instance;

Will do the job. This will first check if there is one, if so it will return the instance else it will create one and next time it will return one.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM