【问题标题】:.NET - Is there any way to create a non-static thread method?.NET - 有没有办法创建非静态线程方法?
【发布时间】:2010-12-10 17:33:59
【问题描述】:

有没有办法在 .NET 中创建非静态线程方法? 请给我看代码。

以下代码不起作用:

ThreadStart ts = delegate { drawFloorAround(); }; 公共无效drawFloorAround() { ... }

给出此错误 -> “字段初始化程序无法引用非静态字段、方法或属性”。 如果我将方法更改为静态,它会起作用。但我不想。

【问题讨论】:

  • 你只需要对 drawFloorAround() 对象的引用;
  • @philip:不,这也不起作用;如果对象是“this”,这似乎是有意的,那么会发生同样的错误。 您不能在字段初始化程序中引用“this”。

标签: c# .net multithreading


【解决方案1】:

... 给出此错误“字段初始值设定项无法引用非静态字段、方法或属性”。

更仔细地阅读错误信息。它准确地告诉你出了什么问题。 字段初始化器不能引用非静态方法。那是因为编译器试图保护你免受这个错误的影响:

class C
{
    int foo;
    int bar = GetBar();
    public C(int newFoo)
    {
        this.foo = newFoo;
    }
    private int GetBar() { return this.foo + 1; }
}

您执行“新 C(123)”。酒吧设置为什么?如果这是合法代码,那么它将被设置为 1,而不是 124。为什么?因为首先 foo 被初始化为零,然后 GetBar() 被调用,然后构造函数体将 this.foo 设置为 123。

为了防止这个错误,在字段初始化器中引用实例方法或字段是非法的。

现在,您可能会合理地指出,在您的代码中,您不使用实例方法,您只引用它。你从来没有真正调用它。这实际上是安全的。但是,C# 的规则被设计得简单而保守;即使我们可以证明这种情况是安全的,我们还是采取保守、简单的方法,并说在字段初始化程序中对实例的 any 引用是非法的。

如果我将方法更改为静态,它可以工作。

正确。在这种情况下,该方法不依赖于尚未设置的实例状态。

但我不想。

好的,那么你唯一的选择就是停止使用字段初始化器。将初始化放在构造函数中;然后您负责确保初始化不会意外使用未初始化状态。

【讨论】:

    【解决方案2】:

    如果您的意思是是否可以使用非静态方法启动线程 - 即实例方法 - 那么是的。但同样的规则也适用于直接调用实例方法——只有当你有实例时才能这样做。例如,如果您在名为foo 的变量中有一个实例,那么您可以这样写:

    ThreadStart ts = delegate { foo.DrawFloorAround(); };
    

    如果您还没有可以使用的实例,那么您必须先创建一个:

    ThreadStart ts = delegate { new Foo().DrawFloorAround(); };
    

    如果您不想创建实例,那么您的方法可能应该是静态的。

    【讨论】:

    • 是的......正是......我希望我能给你 2 票。
    • 这对我有用。新的 Foo().DrawFloorAround.. 谢谢。该方法应该是静态的。但是在我调用画布的方法内部,所以静态的东西有问题。
    【解决方案3】:

    是的

        public class DoSomthing
        {
    
            public void Do()
            {
                Thread t = new Thread(DoInBackground);
                t.Start();
            }
    
            public void DoInBackground()
            {
                // ....
            }
    
        }
    

    【讨论】:

    • 就在这里……以上所有答案对我来说都没用。我不知道在构造函数和外部初始化某些东西会有什么不同。
    【解决方案4】:

    编辑:示例代码中的问题是它是一个字段初始化器。将此代码移至显式构造函数:

    ThreadStart ts;
    public TypeName() {//constructor
        ts = this.SomeMethod;
    }
    private void SomeMethod() {....}
    

    任何方法都可以作为 ThreadStart 作为日志广告,它不需要 args 并返回 void。 IMO 最简单的选择是 lambda 或 anon 方法,因为这允许闭包:

    ThreadStart ts = delegate {
        someObj.DoSomething(x, y, "z");
    };
    

    但是对于返回 void 并且不接受 args 的实例方法:

    var obj = /* init obj */
    ThreadStart ts = obj.SomeMethod;
    

    然后

    var thread = new Thread(ts);
    

    【讨论】:

    • 这不起作用。我将在问题中提供更多详细信息
    • @Alan - 在第二个示例中,只需将 SomeMethod 替换为您的方法名称:drawFloorAround。没有什么可以包含左侧的对象(可能是“this”)。
    【解决方案5】:

    初始化器在构造器之前运行,所以你没有实例可以设置它。在构造函数中设置值,你应该没问题。

    class DoesNotWork {
        public Action ts = Frob; // doesn't work, cannot access non-static method 
        void Frob() { }
    }
    class ThisIsFine {
        public Action ts;
        public ThisIsFine() { ts = Frob; }
        void Frob();
    }
    

    【讨论】:

      【解决方案6】:

      对于从 vb.net 迁移到 C# 的任何人来说,重要的是要注意 VB.net 和 C# 之间的规则已经改变。在 vb.net(恕我直言,更好)规则下,初始化程序在对 mybase.new 的调用和构造函数的以下语句之间运行;字段初始值设定项可以引用当前对象的字段和属性。虽然如果不小心这样做可能会导致问题,但它允许在源代码中与声明相同的位置处理变量初始化(在某些情况下是清理)。任何迁移到 C# 的人都需要认识到初始化程序处理中的这种差异。虽然在 vb.net 中可以正确处理在初始化程序中创建的 iDisposable,但在 C# 中如果没有使用 threadstatic 变量的严重混乱是不可能的。

      【讨论】:

        猜你喜欢
        • 2013-04-01
        • 2013-11-28
        • 1970-01-01
        • 2014-01-17
        • 2011-12-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多