【问题标题】:How does one access a control from a static method?如何从静态方法访问控件?
【发布时间】:2009-06-04 17:37:01
【问题描述】:

我有一个 C# .NET 应用程序,它有一个 MainForm 和一些类。

其中一个类接收来自网络的传入数据消息。我需要将这些消息的文本附加到MainForm 上的多行文本框中。

我可以将消息发送到MainForm 中的方法,方法是将方法设为静态,但静态方法无法访问MainForm 的控件。

TheIncomingDataClass.cs

namespace TheApplicationName
{
     class TheIncomingDataClass
     {

     public void IncomingMessage(IncomingMessageType message)
     {
          TheApplicationName.MainForm.ReceiveMSG(message);
     }

MainForm.cs

public static void ReceiveMSG(string message)
{
     txtDisplayMessages.AppendText(message); //This line causes compile error
}

编译错误:

非静态字段、方法或 属性 'TheApplicationName.MainForm.txtDisplayMessages'

【问题讨论】:

    标签: c# .net winforms


    【解决方案1】:

    静态方法不能访问像 txtDisplayMessages 这样的成员,因为它不是该实例的一部分。我建议您阅读一下静态方法的概念等等,因为这是一个与语言完全无关的概念。最好通过删除 static 修饰符来提供该方法,因为它不需要是静态的 - 它似乎需要由该对象的特定实例调用。

    【讨论】:

    • 一旦我从方法中删除了 static 关键字,我就不能再从 IncomingData 类中调用它。我不明白您所说的“由该对象的特定实例调用”是什么意思,您能进一步解释一下吗?请记住,我需要在收到这些消息后立即将它们发送到文本框,因此该事件将是接收消息。谢谢。
    • 我假设您使用 MainForm 调用它是静态的。 ReceiveMSG()[或只是 ReceiveMSG()]。必须从 MainForm 的特定实例调用它(MainForm mf = new MainForm(); 或类似的,以创建实例)。然后,您可以将其称为 mf。 ReceiveMSG(),将 mf 替换为您命名的 MainForm 实例。无意冒犯,但我认为您需要阅读一些关于面向对象的与语言无关的文献,了解这一切的含义,然后了解 C# 语法与它的关系。
    • 说真的,您应该通过 C# 获取 CLR。它很容易阅读(跳过第一对章节)并详细介绍了这一点以及更多内容!
    • 好的,我想我现在明白了。没有冒犯,我知道我需要阅读/了解更多我仍在这样做的过程中。我的这个项目主要是为了学习。再次感谢。我会去找那本书威尔,谢谢。
    【解决方案2】:

    鉴于您是 C# 新手,我会尽量保持简单。您应该有一个 Program.cs 文件,其中包含一个方法 Main(这将由 Visual Studio 生成)。您需要使其如下所示:

    class Program
    {
        public static readonly MainForm MainForm;
    
        static void Main()
        {
            Application.EnableVisualStyles();
            MainForm = new MainForm(); // These two lines
            Application.Run(MainForm); // are the important ones.
        }
    }
    

    现在,在您收到的消息中,您将可以访问该表单。

     public void IncomingMessage(IncomingMessageType message)
     {
          Program.MainForm.RecieveMSG(message);
     }
    

    然后,表单中的该方法将是一个实例(非静态)方法。例如

     public void RecieveMSG(IncomingMessageType message) // NB: No static
     {
         txtDisplayMessages.Text = message.Text; // Or whatever.
     }
    

    有更好的方法来做到这一点 - 但作为初学者,我认为这是最好的方法。

    静态和实例之间的区别(实例是当你不说静态时)是巨大的。要访问实例方法、字段或属性(在 C# 中统称为成员),您需要拥有包含实例。所以:

     Person p = new Person(); // You now have an instance.
     p.Name = "Fred"; // You are using an instance property.
    

    Static 正好相反,它们在您的应用程序中的任何地方都是相同的(从技术上讲,在同一个 AppDomain 中 - 但如果您是初学者,您暂时不必担心这一点)。您不需要实例即可访问它们(codewidgets 的道具“静态方法只能访问静态成员”)。例如:

     // Where Planet is a class and People is a static property.
     // Somewhat confusingly the Add method is an instance - best left for the student :).
     Planet.People.Add(new Person("Fred")); 
    

    希望这能让您很好地了解静态和实例是什么以及在哪里使用它们。最重要的是尽量避免使用静态成员 - 它们可能会导致维护噩梦。

    微软在这方面的重要概念上有一个完整的write-up

    【讨论】:

    • 拥有静态成员如何造成维护噩梦?
    • 它使重构更加困难。我一次又一次地用它挖了一个洞——尤其是在螺纹方面。最好尽早学习如何在没有太多静态成员的情况下工作。
    • 如果您不了解 OOD 或如何编写可维护的代码,我想您可能会为自己制造一场噩梦,但这将是开发人员的错。就线程而言,开发人员只需要了解如何编写线程安全代码。 .Net 有很多很好的类来帮助解决这个问题。静态成员有自己的位置,理解它们对于一些非常重要的软件设计模式至关重要。我永远不会告诉任何人远离他们。
    • 确实如此。我只是发现这些天使用上下文类和服务提供更容易。可维护性和我所说的“可变形性”不是一回事——我可以教黑猩猩 OOD 以及如何编写可维护的代码。如果您的应用程序的需求(或主机)变化迅速且巨大;静态成员肯定是你的克星。我在那个遗留代码中只有一个静态字段 (Dictionary),它让我很吃惊。为什么?因为它最初是作为独立应用程序开始的,现在它被集成并多次异步调用。应该是一个上下文类。
    【解决方案3】:

    要继续你一直在做的方式,你的“TheIncomingDataClass”应该引用它应该与之交互的MainForm对象。当你创建这个类的实例时(大概来自MainForm的实例方法),你需要传入一个对这个MainForm对象的引用。

    class TheIncomingDataClass{
        MainForm form;
    
        public TheIncomingDataClass(MainForm form){
            this.form = form;
        }
    }
    
    class MainForm : Form{
        MainForm(){
            new TheIncomingDataClass(this);
        }
    }
    

    但是,正如Bugs 所建议的那样,您最好将其设为TheIncomingDataClass 上的活动并从MainForm 订阅它。

    class IncomingMessageEventArgs : EventArgs{
        IncomingMessageType message;
    
        public IncomingMessageType Message{get{return message;}}
    
        public IncomingMessageEventArgs(IncomingMessageType message){
            this.message = message;
        }
    }
    
    class TheIncomingDataClass{
        public event EventHandler<IncomingMessageEventArgs> MessageReceived;
    
        protected virtual void OnMessageReceived(IncomingMessageEventArgs e){
            if(MessageReceived != null)
                MessageReceived(this, e);
        }
    
        public void IncomingMessage(IncomingMessageType message){
            OnMessageReceived(new IncomingMessageEventArgs(message));
        }
    }
    
    class MainForm : Form{
        MainForm(){
            new TheIncomingDataClass().MessageReceived +=
                (s, e)=>txtDisplayMessages.AppendText(e.Message.ToString());
        }
    }
    

    【讨论】:

    • 我正在尝试使用您的方法,除了 MainForm 中的最后一部分之外一切顺利。 "(s, e)=>txtDisplayMessages.AppendText(e.Message.ToString());"这行代码返回许多编译错误。特别是 (s, e) 部分。
    【解决方案4】:

    可以像这样传递对当前表单的引用:

    public static void ReceiveMSG(string message, MainForm mainform)
    {
         mainform.txtDisplayMessages.AppendText(message); 
    }
    

    尽管正如建议的那样,举办活动可能是一种更好的方式。

    【讨论】:

    • 这对我不起作用。我将ReceiveMSG 放在不同的类中,即使MainForm mainform 被声明为参数,我也无法访问它的控件。
    【解决方案5】:

    从表单可以订阅的类中引发一个事件。

    【讨论】:

      【解决方案6】:

      只需删除静态修饰符,您不需要它来实现您的目的。阅读静力学here

      【讨论】:

        【解决方案7】:

        你可以通过去掉 static 关键字来解决这个问题。

        当你看到“静态”时,想想:没有这种类型的实例。

        当你调用一个非静态方法时,你必须显式地使用一些实例。该方法可以使用“this”关键字访问该实例。

        当您调用静态方法时,没有实例 - 您已经放弃了 OO 的界限,现在处于结构化或函数式编程上下文中。如果你想要某个东西的实例,你需要把它作为参数引入。

        【讨论】:

          【解决方案8】:

          我认为您可能对此采取了错误的方法。听起来您正试图从外部进程向客户端推送消息。有办法做到这一点,但它会变得复杂。我的建议是让客户定期轮询具有数据的任何进程 - 可能每 10 秒一次,具体取决于您的需要。这比从服务器推送到客户端要容易得多。

          【讨论】:

          • Jonathan - 如果他使用远程处理,为什么投票会更糟?至于线程,我不确定您建议使用哪种方法,但下面的大多数建议似乎都涉及事件 - 如果您使用事件,您将遇到相同的线程问题。既然他说他是初学者,我只建议最简单的方法——在那儿打一个计时器,每隔 x 秒检查一次新消息。如果您要更新 ui,请调用 invoke - 如果您走事件路线,您将不得不做同样的事情。
          • -嗯,不,计时器在不同的线程上运行。 -您假设数据源在同一台机器上,作者从未定义过。他只是说来源是“来自网络的传入数据消息”——这可能是很多不同的事情,但暗示至少来源不在同一个 appdomain 中。现在 - 鉴于作者经验有限,并且他正在处理 w/ 与多个进程进行通信 - 你认为他会理解如何使用远程事件吗?只是试图抛出一个他可能没有想到的选项会很容易做到。
          • 我的立场是正确的,我已经收回了我的其他 cmets。我确实假设他的网络代码将在同一个 AppDomain 中。在任何情况下:msdn.microsoft.com/en-us/library/…>:“这个 Windows 计时器是为单线程环境设计的[...]”他的 UI 会阻塞。测试 pastebin.com/m61c0884c> 更加容易。
          • 感谢您保持开放的心态。这是计时器上的一个很好的发现,我假设事件是在后台线程上处理的。在这种情况下,我想轮询操作必须非常快,否则如果作者担心在调用期间挂起 UI,他将不得不考虑在不同的线程上运行轮询操作。
          【解决方案9】:

          好的,到此为止。 静态方法只能访问静态成员。您的 ReceiveMSG 方法是静态的。 txtDisplayMessages 不是,因此您无法访问它。 为什么你的方法需要是静态的?不用说,如果您删除将解决您的问题的静态关键字。

          只需将 ReceiveMSG 作为类的一部分,创建该类的实例,然后调用实例上的方法。

          我认为您应该发布您期望的解决方案。

          【讨论】:

            【解决方案10】:
            private void FormMain_Load(object sender, EventArgs e)
            {
                TheIncomingDataClass.SetupControl(textBox1);
            }
            
            public class TheIncomingDataClass
            {
                public static TextBox textbox = new TextBox();
                public static void SetupControl(TextBox txt)
                {
                    textbox = txt;
                }
                public void IncomingMessage(string message)
                {
                    textbox.Text = message;
                }
            }
            

            【讨论】:

            • 欢迎来到 Stack Overflow!仅代码的答案在 SO 上不被认为是高质量的。请详细说明此代码的作用以及它如何帮助解决问题。另外,如果是从某处复制的,请注明出处。
            猜你喜欢
            • 1970-01-01
            • 2012-03-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-07-22
            • 2012-06-29
            • 1970-01-01
            相关资源
            最近更新 更多