Это работает, если вы передаете ссылку на метод напрямую:
Foo4(Foo1);
Это связано с тем, что фактические делегаты с одинаковой формой изначально не считаются совместимыми. Если контракты неявные, компилятор выводит контракт и сопоставляет их. Если они явные (например, объявленные типы), никакой вывод не выполняется - это просто разные типы.
Это похоже на:
public class Foo
{
public string Property {get;set;}
}
public class Bar
{
public string Property {get;set;}
}
Мы видим, что два класса имеют одинаковую сигнатуру и являются "совместимыми", но компилятор видит их как два разных типа, и ничего более.