加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – 如果字段是实例成员,则使用Callback Handler异常

发布时间:2020-12-16 01:39:10 所属栏目:百科 来源:网络整理
导读:希望有人帮助我 如果CallbackHandler.proxy是静态的,那么一切正常: using System;using System.ServiceModel;namespace ConsoleApplication5{ // Define class which implements callback interface of duplex contract public class CallbackHandler : Ser
希望有人帮助我

如果CallbackHandler.proxy是静态的,那么一切正常:

using System;
using System.ServiceModel;

namespace ConsoleApplication5
{
    // Define class which implements callback interface of duplex contract
    public class CallbackHandler : ServiceReference1.IStockServiceCallback
    {
        public static InstanceContext site = new InstanceContext(new CallbackHandler());
        public static ServiceReference1.StockServiceClient proxy = new ServiceReference1.StockServiceClient(site);

        //  called from the service
        public void PriceUpdate(string ticker,double price)
        {
        }  
    }

    class Program
    {
        static void Main(string[] args)
        {
            CallbackHandler cbh = new CallbackHandler();
        }
    }
}

但是,如果我将它声明为实例成员,那么我得到System.TypeInitializationException:CallBackHandler的类型初始化程序’引发了异常. —> System.ArgumentNullException.值不能为null异常

using System;
using System.ServiceModel;

namespace ConsoleApplication5
{
    // Define class which implements callback interface of duplex contract
    public class CallbackHandler : ServiceReference1.IStockServiceCallback
    {
        public static InstanceContext site = new InstanceContext(new CallbackHandler());
        public ServiceReference1.StockServiceClient proxy = new ServiceReference1.StockServiceClient(site);

        //  called from the service
        public void PriceUpdate(string ticker,double price)
        {
        }  
    }

    class Program
    {
        static void Main(string[] args)
        {
            CallbackHandler cbh = new CallbackHandler();
        }
    }
}

知道为什么让CallbackHandler.proxy成为实例成员会引发异常吗?

编辑:

In the 2nd case,the instance
constructor in the line marked (*)
runs before the completion of the
static constructor (yes,it’s
possible),but at that point site is
still not assigned.

因此,在第二种情况下,站点应该初始化为null,而在第一种情况下应该为它分配一个非空值?!

但…

起初我认为静态站点为null(无论代理是实例还是静态成员)只是因为它是用CallbackHandler初始化的,如下所述:

So when CLR tries to instantiate an
instance O ( which it would then
assign to site ),it waits for
static field site to get
initialized,while site waits for
O to get created,which in turn
would initialize site field. Since
this could create a deadlock of sort,
site is “the wiser” and thus gets
set to default value null?!

然后我记得如果代理也是静态的,那么该站点不为null,所以我对正在发生的事情的理解改为:

So when CLR tries to instantiate an
instance O ( which it would then
assign to site ),it waits for
static field site to get initialized
( so that it can assign site’s
reference to its instance member
proxy ). while site waits for O
to get created,which in turn would
initialize site field. Since this
could create a deadlock of sort,CLR
detects this potential deadlock and
sets site to null.

但是后来我运行了你的测试代码,由于某种原因,即使代理不是静态的,也会分配站点(因此不为空):

using System;

namespace ConsoleApplication5
{
    public class InstanceContext
    {
        public InstanceContext(CallbackHandler ch)
        {
            Console.WriteLine("new InstanceContext(" + ch + ")");
        }
    }

    public class StockServiceClient
    {
        public StockServiceClient(InstanceContext ic)
        {
            Console.WriteLine("new StockServiceClient(" + ic + ")");
        }
    }

    // Define class which implements callback interface of duplex contract
    public class CallbackHandler
    {
        public static InstanceContext site = new InstanceContext(new CallbackHandler());
        public StockServiceClient proxy = new StockServiceClient(site);
        public CallbackHandler()
        {
            Console.WriteLine("new CallbackHandler()");
        }
        static CallbackHandler()
        {
            Console.WriteLine("static CallbackHandler()");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(CallbackHandler.site == null); // returns false
        }
    }
}

到底是怎么回事?

解决方法

问题出在这一行:

public static InstanceContext site = new InstanceContext(new CallbackHandler());

这条线真的很邪恶!

必须在执行上面给出的行的新CallbackHandler()之前完成CallbackHandler的静态初始化(因为这会创建一个实例).但是这一行隐含了静态构造函数的一部分!所以我认为.NET运行时无法执行此行,并使站点未初始化(或稍后初始化).这就是代理初始化站点仍为空的原因.

顺便说一下,我不确定是否定义了静态初始化的顺序.考虑这样一个例子:

class Test
{
    static Twin tweedledum = new Twin(tweedledee);
    static Twin tweedledee = new Twin(tweedledum);
}

编辑:
paragraph 10.4.5.1 of C# language specs表示静态字段是按文本顺序初始化的,而不是考虑依赖性.

编辑:
找到了! part 10.11 of C# language specs明确指出:

It is possible to construct circular dependencies that allow static fields with variable initializers to be observed in their default value state.

我们所做的确实是一个循环依赖:CallbackHandler依赖于它自己.因此,您获得的行为实际上已记录在案,并且符合标准.

编辑:
奇怪的是,当我测试代码(here和here)时,我在实例构造函数完成后运行静态构造函数.这怎么可能?

编辑:
得到this问题的答案,我可以解释会发生什么.

在第一种情况下,您的代码被隐式重写为

public static InstanceContext site;
public static ServiceReference1.StockServiceClient proxy;
static CallbackHandler()
{
    site = new InstanceContext(new CallbackHandler());
    proxy = new ServiceReference1.StockServiceClient(site);
}

在第二种情况下,你得到

public static InstanceContext site;
public ServiceReference1.StockServiceClient proxy;
static CallbackHandler()
{
    site = new InstanceContext(new CallbackHandler()); // (*)
}
public CallbackHandler()
{
    proxy = new ServiceReference1.StockServiceClient(site);
}

在第二种情况下,标记为(*)的行中的实例构造函数在静态构造函数完成之前运行(是的,它是可能的),但此时仍未分配站点.

因此,基本上在您的第二个代码变体中,您在每个实例中都有一个单独的代理,它指向静态站点,而静态站点又引用了另一个CallbackHandler的“默认”实例.这真的是你想要的吗?也许,你只需要拥有一个实例字段吗?

因此,在代码的第二个变体中会发生以下情况:

>主要开始.
>行之前CallbackHandler cbh = new CallbackHandler();调用CallbackHandler的静态构造函数
>计算新InstanceContext的参数

>执行构造函数new CallbackHandler()
>作为构造函数的一部分,使用新的ServiceReference1.StockServiceClient(站点)初始化代理,site的值为null.抛出,但让我们暂时忘记这一点,并考虑接下来会发生什么.

>使用计算的参数,将调用构造函数新的InstanceContext
>构造函数的结果被分配给site,现在不再是null.这样就完成了静态构造函数
>现在,在Main中调用的构造函数可以启动.

>作为其中的一部分,构建了一个新的代理,现在具有非null的站点值

>刚刚创建的CallbackHandler被赋值给变量cbh.

在您的情况下,ServiceReference1.StockServiceClient(site)抛出因为site为null;如果它不关心空值,代码将如上所述运行.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读