NullReferenceException : public class Tester { public string Property { get; set; } public void Foo() { this.Property = "Some string"; // NullReferenceException } } NullReferenceException . What business, I think - really rantaym stopped checking the presence of an instance before calling instance methods?Tester class there is an instance method Foo and an instance property Property . Someone called the Foo method, but a this.Property found on the call to this.Property , which led to the generation of the NullReferenceException exception.this == null in the string, and therefore the string this.Property = smth cannot access the property. But for a C # programmer, this sounds completely impossible - after all, if the Foo method was somehow called, the class instance exists and this cannot be null ! How could you call a method on null ? static class Program { static void Main() { Tester t = null; t.Foo(); } } NullReferenceException on the string t.Foo(); , but does not enter the Foo method. It that turns out, under any conditions rantaym forgot to execute check on null ?null ), but the C ++ / CLI compiler, with which the code was compiled, which in the original way called the Foo method. Yes, the participation of C ++ / CLI in this story would immediately have caused a lot of suspicion, and I didn’t say anything special about it at first, so that it was more interesting :)Tester class): int main() { Tester ^t = nullptr; t->Foo(); } NullReferenceException inside the Foo method, just like in the original case. That is, the instance method Foo somehow called from the zero reference, bypassing any checks.ildasm and parse the program code in C #. I give a full listing of the Program.Main method (in the comments I gave the source code lines corresponding to the bytecode): .method private hidebysig static void Main() cil managed { .entrypoint // Code size 11 (0xb) .maxstack 1 .locals init ([0] class [Shared]ThisIsNull.Tester t) IL_0000: nop IL_0001: ldnull IL_0002: stloc.0 // Tester t = null; IL_0003: ldloc.0 IL_0004: callvirt instance void [Shared]ThisIsNull.Tester::Foo() // t.Foo() IL_0009: nop IL_000a: ret } IL_0004 . We see that the compiler has called the Foo method using the callvirt . Now compare with the corresponding code in C ++ / CLI: .method assembly static int32 modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl) main() cil managed { .vtentry 1 : 1 // Code size 12 (0xc) .maxstack 1 .locals ([0] class [Shared]ThisIsNull.Tester t) IL_0000: ldnull IL_0001: stloc.0 // Tester ^t = nullptr; IL_0002: ldnull IL_0003: stloc.0 // t = nullptr; IL_0004: ldloc.0 IL_0005: call instance void [Shared]ThisIsNull.Tester::Foo() // t->Foo(); IL_000a: ldc.i4.0 IL_000b: ret } callvirt , but via call .callvirt is actually intended for virtual calls. However, it has another small feature - since virtual calls are usually made to the CLI via a virtual method table, it is the responsibility of the callvirt also check the null reference and throw a NullReferenceException exception if something went wrong.call instruction simply calls the method without checking the links (and not using the virtual dispatch mechanisms).callvirt instruction feature and therefore generates it for all calls in general (except for static and explicit calls to the base class methods through the base. ) - just because it protects the code from the method call of the zero reference. At the same time, the C ++ / CLI compiler operates according to the good old laws of the System.String , which once caused questions on StackOverflow , is also interesting: public bool Equals(String value) { if (this == null) //this is necessary to guard against reverse-pinvokes and throw new NullReferenceException(); //other callers who do not use the callvirt instruction if (value == null) return false; if (Object.ReferenceEquals(this, value)) return true; return EqualsHelper(this, value); } null in this way. The fact is that the comparison of strings in the EqualsHelper method EqualsHelper implemented using an unsafe code, which may well try to access the memory section at the zero address, which will certainly lead to all sorts of bad consequences.((string)null).Equals(null) call could return false , rather than fall from a NullReferenceException , as it should.this != null even when invoking instance methods and properties.this == null for various reasons (code generation, reflection, compilers of other languages), and you need to be ready for this. If you are developing a library intended for widespread use in an interop environment, you may even need to add null checks to public methods of externally accessible classes.Source: https://habr.com/ru/post/252249/
All Articles