Generics are not dynamic typing. What overload for the call is frozen at compile time. When the program starts later, even if the variable contains a more specific type of runtime, it does not matter, since the overload was fixed at compile time.
Your method:
public string TestExt() { return this.Ext(a => a.Content); }
must be bound at compile time to one specific Ext overload. Since all we know about T ( a.Content type) in the MyClass<T> class is that it converts to object , in fact there is only one overload, so this is easy for the compiler.
From now on, the body of the TestExt method TestExt hard-coded to call this particular Ext overload.
EDIT: here is a much simpler example:
static void Main() { IEnumerable<object> e = new List<object>(); var r = Generic(e); } static string Generic<T>(T x) { return Overloaded(x); } static string Overloaded(IEnumerable<object> x) { return "Enumerable"; } static string Overloaded(object x) { return "Object"; }
and, as you understand, r becomes "Object" .
(If you are constrained by T some way, for example where T : IEnumerable<object> , everything will be different).
The same goes for operators. For example, the == operator is overloaded in the sense that it works in one case for generic reference types, and in another for strings. So, in the example below, the == operator plays the role of Overloaded from:
static void Main() { string str1 = "abc"; string str2 = "a"; str2 += "bc";
where b2 becomes false .