1: namespace ConsoleApplication1
2: {
3: class CTest
4: {
5: public CTest(int i) {}
6: }
7: }
I think that first confusing thing is the usage of the new operator. It may give you a feeling that something here is different. Take a look at the line 7 of this example. What do you think, is this method is thread safe?
1: class C
2: {
3: private CTest c;
4:
5: public void Foo()
6: {
7: c = new CTest(1);
8: }
9: }
This is what happens behind the stage in method Foo(). Here is the Foo() code in a little details
1 .method public hidebysig instance void Foo() cil managed
2 {
3 .maxstack 8
4 L_0000: nop
5 L_0001: ldarg.0
6 L_0002: ldc.i4.1
7 L_0003: newobj instance void ConsoleApplication1.CTest::.ctor(int32)
8 L_0008: stfld class ConsoleApplication1.CTest ConsoleApplication1.CTest::c
9 L_000d: ret
10 }
It is clear that line 7 with opcode newobj creates and pushes a new instance of CTest into the stack and exactly one line later, the local variable c is assigned with a brand new value. If multiple threads were calling CTest::Foo() the result would be unpredictable.
We need to consider one more subtle issue: constructor parameters. If one of the parameters is a reference type, it means that external thread may change the referenced object and make the constructor invalid.
1: class Test
2: {
3: public Test(IList items)
4: {
5: if(items.Count > 0)
6: {
7: // do something special
8: }
9: }
10: }
Conclusion:
Constructor is not an exception case then we deal with multi threaded environment. I have no idea why and how this simple issue can confuse anyone but since I witnessed a long discussion regarding this issue, I contribute this post to remove any kind of hesitations. Enjoy.
Post a Comment