this == null inside .NET instance method - why is that possible?

后端 未结 5 848
别那么骄傲
别那么骄傲 2020-12-14 15:22

I\'ve always thought that it\'s impossible for this to be null inside instance method body. Following simple program demonstrates that it is possible. Is this s

相关标签:
5条回答
  • 2020-12-14 15:33

    this is a readonly reference in C# classes. Accordingly and as expected this can be used like any other references (in read only mode) ...

    this == null // readonly - possible
    this = new this() // write - not possible
    
    0 讨论(0)
  • 2020-12-14 15:35

    Because you're passing null into the firstArgument of Delegate.CreateDelegate

    So you're calling an instance method on a null object.

    http://msdn.microsoft.com/en-us/library/74x8f551.aspx

    If firstArgument is a null reference and method is an instance method, the result depends on the signatures of the delegate type type and of method:

    If the signature of type explicitly includes the hidden first parameter of method, the delegate is said to represent an open instance method. When the delegate is invoked, the first argument in the argument list is passed to the hidden instance parameter of method.

    If the signatures of method and type match (that is, all parameter types are compatible), then the delegate is said to be closed over a null reference. Invoking the delegate is like calling an instance method on a null instance, which is not a particularly useful thing to do.

    0 讨论(0)
  • 2020-12-14 15:44

    Try the documentation for Delegate.CreateDelegate() at msdn.

    You're "manually" calling everything, and thus instead of passing an instance in for the this pointer, you're passing null. So it can happen, but you have to try really really hard.

    0 讨论(0)
  • 2020-12-14 15:46

    this is a reference, so there is no problem with its being null from the perspective of the type system.

    You may ask why NullReferenceException was not thrown. The full list of circumstances when CLR throws that exception is documented. Your case is not listed. Yes, it is a callvirt, but to Delegate.Invoke (see here) rather than to Bar, and so the this reference is actually your non-null delegate!

    The behavior you see has an interesting implementational consequence for CLR. A delegate has a Target property (corresponds to your this reference) that is quite frequently null, namely when the delegate is static (imagine Bar be static). Now there is, naturally, a private backing field for the property, called _target. Does _target contain a null for a static delegate? No it doesn't. It contains a reference to the delegate itself. Why not null? Because a null is a legitimate target of a delegate as your example shows and CLR does not have two flavors of a null pointer to distinguish the static delegate somehow.

    This bit of trivium demonstrates that with delegates, null targets of instance methods are no afterthought. You may still be asking the ultimate question: but why they had to be supported?

    The early CLR had an ambitious plan of becoming, among others, the platform of choice even for sworn C++ developers, a goal that was approached first with Managed C++ and then with C++/CLI. Some too challenging language features were omitten, but there was nothing really challenging about supporting instance methods executing without an instance, which is perfectly normal in C++. Including delegate support.

    The ultimate answer therefore is: because C# and CLR are two different worlds.

    More good reading and even better reading to show the design allowing null instances shows its traces even in very natural C# syntactic contexts.

    0 讨论(0)
  • 2020-12-14 15:47

    Sure you can call into a method if you are using the call IL instruction or the delegate approach. You will set this booby trap only off if you try to access member fields which will give you the NullReferenceException you did seek for.

    try

     int x;
     public void Bar()
     {
            x = 1; // NullRefException
            Debug.Assert(this == null);
     }
    

    The BCL does even contain explicit this == null checks to aid debugging for languages which do not use callvirt (like C#) all the time. See this question for further infos.

    The String class for example has such checks. There is nothing mysterious about them except that you will not see the need for them in languages like C#.

    // Determines whether two strings match. 
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 
    public override bool Equals(Object obj)
    {
        //this is necessary to guard against reverse-pinvokes and
        //other callers who do not use the callvirt instruction
        if (this == null)
            throw new NullReferenceException();
    
        String str = obj as String;
        if (str == null) 
            return false;
    
        if (Object.ReferenceEquals(this, obj)) 
            return true;
    
        return EqualsHelper(this, str);
    }
    
    0 讨论(0)
提交回复
热议问题