Hope someone helps me with this
If CallbackHandler.proxy is static, then everything works fine:
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();
}
}
}
But if I declare it as an instance member, then I get System.TypeInitializationException: The type initializer for CallBackHandler’ threw an exception. ---> System.ArgumentNullException. Value cannot be null exception
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();
}
}
}
Any idea why making CallbackHandler.proxy an instance member throws an exception?
EDIT:
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.
Thus in second case site should be initialized to null, while in first case it should be assigned a non-null value?!
But…
At first I thought static site was null ( regardless of whether proxy was instance or static member ) simply because it was initialized with CallbackHandler, as explained here:
So when CLR tries to instantiate an
instanceO( which it would then
assign tosite), it waits for
static fieldsiteto get
initialized, whilesitewaits for
Oto get created, which in turn
would initializesitefield. Since
this could create a deadlock of sort,
siteis “the wiser” and thus gets
set to default value null?!
Then I remembered that site is not null if proxy is also static, so my understanding of what’s happening changed to:
So when CLR tries to instantiate an
instanceO( which it would then
assign tosite), it waits for
static fieldsiteto get initialized
( so that it can assignsite’s
reference to its instance member
proxy). whilesitewaits forO
to get created, which in turn would
initializesitefield. Since this
could create a deadlock of sort, CLR
detects this potential deadlock and
setssiteto null.
But then I’ve run your test code and for some reason site is assigned ( thus is not null ) even if proxy is not static:
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
}
}
}
What is going on?
The problem is with this line:
This line is really evil!
The static initialization of
CallbackHandlermust be finished before thenew CallbackHandler()from the line given above is executed (because this would create an instance). But this line is implicitly a part of the static constructor! So I suppose the .NET runtime cannot execute this line, and leavessiteuninitialized (or initialized later). That’s why at theproxyinitializationsiteis stillnull.By the way, I am not sure if the order of static initializations is defined at all. Consider such an example:
Edit:
the paragraph 10.4.5.1 of C# language specs says that the static fields are initialized in the textual order, not considering the dependencies.
Edit:
Eureka! The part 10.11 of C# language specs clearly states:
What we did is indeed a circular dependency:
CallbackHandlerdepends on itself. So the behaviour you get is actually documented and is according to the standard.Edit:
Strange enough, when I test the code (here and here), I get static constructor running after instance constructor finishes. How is this possible?
Edit:
having got the answer to this question, I can explain what happens.
In the 1st case, your code is implicitly rewritten as
In the 2nd case, you get
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
siteis still not assigned.So, basically in your second variant of code you have at each instance a separate proxy, which points to a static site, which in turn references some another “default” instance of
CallbackHandler. Is that really what you want? Maybe, you just need to havesitean instance field as well?So, in the 2nd variant of the code the following happens:
CallbackHandler cbh = new CallbackHandler();the static constructor ofCallbackHandleris callednew InstanceContextis calculatednew CallbackHandler()is executedproxyis initialized with thenew ServiceReference1.StockServiceClient(site), the value ofsiteisnull. This throws, but let’s forget about this just now, and consider what would happen next.new InstanceContextis calledsite, which is now notnullany more. This finishes the static constructorMaincan start.proxyis constructed, now with non-nullvalue ofsiteCallbackHandleris assigned to the variablecbh.In your case,
ServiceReference1.StockServiceClient(site)throws becausesiteisnull; if it wouldn’t care aboutnulls, the code would run as it is described above.