【问题标题】:How do I call an instance method from another classes' static method (i.e. have only one object)?如何从另一个类的静态方法(即只有一个对象)调用实例方法?
【发布时间】:2010-01-15 09:15:02
【问题描述】:

我的 form1 类包含一堆需要保存的数据,因此一次只能运行一个实例。

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,不是我的代码,里面包含了一堆键盘钩子需要的静态方法。

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

因为 HookCallBack 方法是静态的,所以 Form1.newKeyPress() 也需要是静态的。

但是如果 newKeyPress 是静态的,我就无法访问我需要的数据!我不想在这里声明一个新的 form1 对象,因为这会给我不同版本的数据。对吗?

我不是面向对象的专家。我应该如何格式化它,以确保所有 InterceptKey 的 form1 方法调用都转到我想要的 form1 对象?

谢谢,如果您需要更多信息,请告诉我!

【问题讨论】:

  • 我认为您将 AKA 与即大声笑混淆了
  • 因为 HookCallBack 方法是静态的,所以 Form1.newKeyPress() 也需要是静态的。 - 不正确 。如果您想使用 Form1 类名作为调用者,newKeyPress 应该是静态的。但总的来说,男人很难理解你想要什么......你能简化/澄清你的问题吗?你的目标是什么?
  • @cksubs...这真的取决于你在哪里!这是早上在这里大声笑
  • 除非我创建一个新的 form1 对象,否则它必须是静态的,对吗?我不想那样做,所以它必须是静态的?
  • 如何将InterceptKeysHookCallback 联系起来?

标签: c# oop formatting


【解决方案1】:

您有两个设计问题:

如何从静态方法调用实例方法

因为 HookCallBack 方法是 静态的,Form1.newKeyPress() 需要 也是静态的。

您可以将主表单的一个实例传递给您的 HookCallBack 方法,您只需向您的静态方法添加一个附加参数:

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

这实际上是首选策略。无论您的方法和类在哪里依赖于其他对象,您都应该将您的依赖项传递到您的方法中,而不是从全局状态中提取它们。

除此之外,您可以遍历 Application.OpenForms 集合并找到您要查找的表单,如下所示:

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

如何一次打开一个表单实例

其他人建议将您的表单设为静态 - 这是一种方法,但它是一种不好的方法。静态表单在处理时不会被垃圾回收,您必须在显示/隐藏表单时实现自己的初始化/重置方法,如果静态表单引用了其他对象,那么您的应用程序将慢慢泄漏内存,among other things .我实际上推荐这样的东西:

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

所以您的 FormFactory 控制表单的生命周期,现在您可以使用 new FormFactory.GetForm1() 获取 Form1 的现有或新实例。

【讨论】:

  • 哦,我喜欢这样。我没有走那条路,因为我无法从HookCallBack 访问任何实例变量,但我忘记了你可以在最初调用它时将它作为参数传递。唯一的问题是我没有写那个代码,看起来private static LowLevelKeyboardProc _proc = HookCallback;就是它的调用方式。如何将表单参数添加到这样的调用中?
  • @cksubs: LowLevelKeyboardProc 是您的项目中定义的委托(可能),类似于publc delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam)。您需要找到委托的定义(大多数时候您可以右键单击并选择“转到定义”)并将您的表单参数添加到参数列表中。编译你的项目,你应该得到一个或多个编译错误,表明你需要在任何地方传递你的新表单参数。
  • @cksubs:现在我想起来了,我上面的建议很愚蠢。上面的一个重要重构意味着我们完全错误地解决了这个问题。这个答案同样可以很好地解决您的问题,因为您的 API 没有任何修改:stackoverflow.com/questions/2070481/…
【解决方案2】:

将按键传递给其他表单

在我看来,您基本上只是将按键传递给您的表单,这意味着一种基本的消息通知/观察者模式。也许你只需要一个更好的设计模式。请尝试以下操作:

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

你猜怎么着?现在您的HookCallBack 方法甚至不需要知道表单的存在。相反,您的表单将自己注册到事件处理程序中:

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

记住,您必须在表单关闭时解开表单事件处理程序,否则表单不会被垃圾收集,并且您会遇到很多奇怪的问题,引发不可见表单上的事件.

【讨论】:

  • 我不知道您的要求,但我可能应该评论说我上面的代码不是线程安全的(即在竞争条件下,我们的事件处理程序可能通过 null 检查,但仍然抛出当我们调用它时出现空引用异常)。见stackoverflow.com/questions/786383/c-events-and-thread-safety
  • 我喜欢这个,事件驱动模型实际上是我想要做的,所以我很高兴做出改变......虽然有麻烦。我注意到您将我的InterceptKeys hook = new InterceptKeys(); 从 form1 构造函数中取出,这是故意的吗?没有它,我根本不会挂上键盘,有了它,我就不会进入if (OnHookCallBack != null) 声明。我错过了什么吗?
  • 哦,另外,“Backs”的大写还有另一个潜在的错误来源。我已经对它们进行了标准化以便编译,但我希望我没有因此而搞砸它。
  • 我还将所有 MessageHooks 更改为 InterceptKeys,因为这是该类之前的名称。
  • 朱丽叶一定在睡觉......其他人注意到我在这段代码中遗漏了什么吗?这么近……
【解决方案3】:

根据您所写的,我认为这会起作用:

Form1 中,有一个Form1 类型的静态成员将保存实例:

private static Form1 instance;

Form1 构造函数中,将此静态成员设置为正在创建的实例:

public Form1()
{
    // Existing code

    Form1.instance = this;
}

现在,将newKeyPress设为静态,并使用静态成员查找实际的表单实例:

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

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

【讨论】:

  • -1: 静态实例!= 在给定时间打开的一个表单实例。恰当的例子:考虑一个在应用程序的整个生命周期中多次打开和关闭的对话框,如果它没有显示,我们将实例化一个新对话框,否则将焦点/激活现有对话框。几乎没有静态形式比非静态形式更可取的情况。
  • 有趣。我并不关心只有一个实例的概念,而是执行,即始终能够访问我的数据。你的观点会阻止我这样做吗?如果是这样,考虑到限制,您有更好的建议吗?
【解决方案4】:

我觉得有点像在 Form1 类中:

private static Form1 instance;


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

在你使用它们的类中:

Form1 form1 = Form1.Instance;

将完成这项工作。这将首先检查是否有一个,如果有,它将返回实例,否则它将创建一个,下次它会返回一个。

【讨论】:

    猜你喜欢
    • 2010-12-24
    • 1970-01-01
    • 1970-01-01
    • 2013-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-30
    • 1970-01-01
    相关资源
    最近更新 更多