Вчера исправлял баг, проявил он себя весьма интересно. Но продемонстрировать его можно на достаточно простом примере:

static void Main(string[] args)
{
    Print(new[] { 1, 2, 3 });
    Console.ReadKey();
}
static voidPrint<T>(T t)
{
    Console.WriteLine(t);
}
static voidPrint<T>(IEnumerable<T> t)
{
    foreach (var item in t)
    {
        Console.WriteLine(item);
    }
}

При запуске вместо ожидаемых 1, 2, 3 мы увидим:

Т.к. мы не задали ограничений на T, то и IEnumerable тоже приводится к T объявленному в первом методе. Накладывание ограничений на T позволило бы узнать о этой проблеме на этапе компиляции. Изменим код:

class Demo
{
    public int X { get; set; }
}
static void Main(string[] args)
{
    Print(new[] { new Demo { X = 1 }, new Demo { X = 2 }, new Demo { X = 3 } });
    Console.ReadKey();
}
static voidPrint<T>(T t) where T : Demo
{
    Console.WriteLine(t);
}
static voidPrint<T>(IEnumerable<T> t) where T : Demo
{
    foreach (var item in t)
    {
        Console.WriteLine(item);
    }

}

В данном примере я явно указал к чему должен приводиться T (к Demo или его потомкам). И вот вместо ошибки времени выполнения, мы получаем ошибку компиляции:

Error CS0311 The type ‘ConsoleApp1.Program.Demo[]’ cannot be used as type parameter ‘T’ in the generic type or method ‘Program.Print(T)’. There is no implicit reference conversion from ‘ConsoleApp1.Program.Demo[]’ to ‘ConsoleApp1.Program.Demo’.

Как написано в заголовке, надо быть аккуратнее с Generic, максимально накладывать на них ограничения и не использовать в перегруженных методах, особенно в таком виде. А то вместо строгой типизации получим не строгую.