C# 静态作用域问题

3
使用 WPF 和 .NET 4.0。
我正在使用 WebClient 下载一些数据,并使用 DownloadStringCompletedEventHandler 在完成后触发我的 DownloadCompletedCallback 函数。
我遇到的问题是,当调用 DownloadCompletedCallback 时,我试图设置主窗体上标签的内容,但出现了错误。
非静态字段、方法或属性“Armory.MainWindow.lblDebug”需要对象引用。
我知道这是因为函数 DownloadCompletedCallback 被声明为静态,但我不明白为什么会有影响。
以下是我正在使用的代码。
public static void DownloadHTML(string address)
{
    WebClient client = new WebClient();

    client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(DownloadCompletedCallback);

    client.DownloadStringAsync(new Uri(address));
}

private static void DownloadCompletedCallback(Object sender, DownloadStringCompletedEventArgs e)
{
    if (!e.Cancelled && e.Error == null)
    {
        lblDebug.Content = (string)e.Result;
    }
}
5个回答

3
我知道这是因为DownloadCompletedCallback函数被声明为静态的,但我不明白为什么这很重要。
根据C#规范:
使用static修饰符声明的方法是静态方法。静态方法不对特定实例进行操作,只能直接访问静态成员。未使用static修饰符声明的方法是实例方法。实例方法对特定实例进行操作,可以访问静态和实例成员。在调用实例方法时,可以显式地访问该实例作为this。在静态方法中引用this是错误的。
这是因为静态方法不是对象的一部分,所以它们无法与任何对象进行交互。它们与没有状态概念的类绑定,因此当您调用它时,静态方法不知道应与哪个对象的非静态对象变量进行交互。
以下是禁止使用静态方法的示例:
Class Example{...}

var ExampleOne = new Example();
var ExampleTwo = new Example();

Example.CallStaticMethod();

那么现在的问题是它应该与哪些非静态变量交互呢?应该是ExampleOne还是ExampleTwo,还是应该抛出一个空引用异常。在前两种情况下,系统无法知道应该与哪个进行交互,因为您没有指定它(否则它将成为实例方法)。对于第三种情况,由于需要实例才能调用它,所以它并不是真正的静态。因此,访问非静态方法、属性等必须被禁止,因为存在太多的不确定性。


昨晚很晚的时候,由于某种原因我认为DownloadCompletedCallback必须是静态的。结果发现,没有必要将所有函数都声明为静态的,只需删除所有静态声明即可正常工作。然而,这个答案帮助我更好地理解了静态函数。谢谢。 - castis

2
静态方法存在于类级别,没有任何特定用户控件实例的知识。所有标签等都在您的用户控件类的特定实例上声明;在应用程序的各个地方可能有许多这样的声明。静态方法存在于您的用户控件的所有这些实例中。如果您有例如5个用户控件存在于各种地方,则所有这些控件共享相同的静态方法,并且该方法无法访问刚刚调用它的特定实例。

0

在静态方法中无法使用 lblDebug。相反,您可以更改 DownloadHTML 方法以接受回调函数:

public static void DownloadHTML(
     string address, 
     DownloadStringCompletedEventHandler callWhenCompleted)
{
    WebClient client = new WebClient();

    client.DownloadStringCompleted += 
        new DownloadStringCompletedEventHandler(callWhenCompleted);

    client.DownloadStringAsync(new Uri(address));
}

private void DownloadCompletedCallback(
    Object sender, DownloadStringCompletedEventArgs e)
{
    if (!e.Cancelled && e.Error == null)
    {
        lblDebug.Content = (string)e.Result;
    }
}

使用:

DownloadHTML(
     "https://dev59.com/Y1TTa4cB1Zd3GeqPv9OF",
     this.DownloadCompletedCallback);

0

这很重要,因为在该类的不同实例中可能有许多不同的lblDebug 。您的函数不绑定到任何特定实例(因为已将其声明为static),因此代码无法猜测您指的是哪个lblDebug

如果您只想要一个单独的lblDebug ,那么您也可以将其声明为静态 - 但在执行此操作之前,请仔细考虑其含义!


0
我认为这也是因为您无法访问lblDebug,因为它在另一个线程上。(DownloadCompletedCallback是异步的,因此在另一个线程上执行)。您需要使用其父对象的调度程序对象调用lblDebug。不过,您需要搜索调用,因为我这里没有Visual Studio提供确切的代码。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接