This is a compiler bug. Here's my simplified reproduction:

{$APPTYPE CONSOLE} type TProc = reference to procedure; TOnObject = procedure of object; procedure Invoke(Proc: TProc); begin if Assigned(Proc) then Proc(); end; procedure CallInvokeOnObject(OnObject: TOnObject); begin Invoke(OnObject); end; begin Invoke(nil); // succeeds CallInvokeOnObject(nil); // results in AV end.

You might wonder why I simplified. Your code was a superb reproduction of the problem. However, I wanted to make it absolutely as simple as possible so that I really could be sure that the problem was what I believe it to be. So I removed the generics and the classes.

Now, the test using Assigned is correct. You are right to expect that it will behave as you intend. The problem is that when the compiler generates code to call Invoke from CallInvokeOnObject , it needs to wrap the method of object in a reference procedure interface. In order to do this correctly it would need to test whether or not the method of object is assigned. If not then no wrapper interface should be created and Invoke should be passed nil .

The compiler fails to do that. It unconditionally wraps the method of object in a reference procedure interface. You can see this in the code emitted for CallInvokeOnObject .

Project1.dpr.16: begin // this is the beginning of CallInvokeOnObject 004064D8 55 push ebp 004064D9 8BEC mov ebp,esp 004064DB 6A00 push $00 004064DD 53 push ebx 004064DE 33C0 xor eax,eax 004064E0 55 push ebp 004064E1 683B654000 push $0040653b 004064E6 64FF30 push dword ptr fs:[eax] 004064E9 648920 mov fs:[eax],esp 004064EC B201 mov dl,$01 004064EE A1F4634000 mov eax,[$004063f4] 004064F3 E8DCDAFFFF call TObject.Create 004064F8 8BD8 mov ebx,eax 004064FA 8D45FC lea eax,[ebp-$04] 004064FD 8BD3 mov edx,ebx 004064FF 85D2 test edx,edx 00406501 7403 jz $00406506 00406503 83EAF8 sub edx,-$08 00406506 E881F2FFFF call @IntfCopy 0040650B 8B4508 mov eax,[ebp+$08] 0040650E 894310 mov [ebx+$10],eax 00406511 8B450C mov eax,[ebp+$0c] 00406514 894314 mov [ebx+$14],eax Project18.dpr.17: Invoke(OnObject); 00406517 8BC3 mov eax,ebx 00406519 85C0 test eax,eax 0040651B 7403 jz $00406520 0040651D 83E8E8 sub eax,-$18 00406520 E8DFFDFFFF call Invoke

That call to TObject.Create is what wraps the method of object in a reference procedure interface. Note that the interface is created unconditionally and then passed to Invoke .

There's no way for you work around this from inside Invoke . By the time the code reaches there it's too late. You cannot detect that the method is not assigned. This should be reported to Embarcadero as a bug.