Ok, so I just ran into the following problem that raised an eyebrow.
For various reasons I have a testing setup where Testing classes in a TestingAssembly.dll depend on the TestingBase class in a BaseTestingAssembly.dll. One of the things the TestBase does in the meantime is look for a certain embedded resource in its own and the calling assembly
So my BaseTestingAssembly contained the following lines...
public class TestBase {
private static Assembly _assembly;
private static Assembly _calling_assembly;
static TestBase() {
_assembly = Assembly.GetExecutingAssembly();
_calling_assembly = Assembly.GetCallingAssembly();
}
}
Static since I figured, these assemblies would be the same over the application's lifetime so why bother recalculating them on every single test.
When running this however I noticed that both _assembly and _calling_assembly were being set to BaseTestingAssembly rather than BaseTestingAssembly and TestingAssembly respectively.
Setting the variables to non-static and having them initialized in a regular constructor fixed this but I am confused why this happened to begin this. I thought static constructors run the first time a static member gets referenced. This could only have been from my TestingAssembly which should then have been the caller. Does anyone know what might have happened?
A static constructor is used to initialize any static data, or to perform a particular action that needs to be performed only once. It is called automatically before the first instance is created or any static members are referenced.
Java constructor can not be static One of the important property of java constructor is that it can not be static. We know static keyword belongs to a class rather than the object of a class. A constructor is called when an object of a class is created, so no use of the static constructor.
Static constructors can use optional arguments. Overloaded constructors cannot use optional arguments. If we do not provide a constructor, then the compiler provides a zero-argument constructor.
When a data member is shared among different instances it is imperative that data should be consistent among all the instances of the class. And also there is no way to call static constructor explicitly. Therefore the purpose of having a parameterized static constructor is useless.
The static constructor is called by the runtime and not directly by user code. You can see this by setting a breakpoint in the constructor and then running in the debugger. The function immediately above it in the call chain is native code.
Edit: There are a lot of ways in which static initializers run in a different environment than other user code. Some other ways are
In general, it's probably best not to use them for anything too sophisticated. You can implement single-init with the following pattern:
private static Assembly _assembly;
private static Assembly Assembly {
get {
if (_assembly == null) _assembly = Assembly.GetExecutingAssembly();
return _assembly;
}
}
private static Assembly _calling_assembly;
private static Assembly CallingAssembly {
get {
if (_calling_assembly == null) _calling_assembly = Assembly.GetCallingAssembly();
return _calling_assembly;
}
}
Add locking if you expect multithreaded access.
I think the answer is here in the discussion of C# static constructors. My best guess is that the static constructor is getting called from an unexpected context because:
The user has no control on when the static constructor is executed in the program
Assembly.GetCallingAssembly() simply returns the assembly of the second entry in the call stack. That can very depending upon where how your method/getter/constructor is called. Here is what I did in a library to get the assembly of the first method that is not in my library. (This even works in static constructors.)
private static Assembly GetMyCallingAssembly()
{
Assembly me = Assembly.GetExecutingAssembly();
StackTrace st = new StackTrace(false);
foreach (StackFrame frame in st.GetFrames())
{
MethodBase m = frame.GetMethod();
if (m != null && m.DeclaringType != null && m.DeclaringType.Assembly != me)
return m.DeclaringType.Assembly;
}
return null;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With