Дженерики и литье

Почему следующий компилятор?

public IList<T> Deserialize<T>(string xml)
{              
    if (typeof(T) == typeof(bool))
        return (IList<T>)DeserializeBools(xml);

    return null;
}

private static IList<bool> DeserializeBool(string xml) { ... do stuff ... }

Но это не

public MyClass<T> GetFromDb<T>(string id)
{
    if (typeof(T) == typeof(bool))
        return (MyClass<T>)GetBoolValue(id);  <-- compiler error here

    return null;
}

private static MyClass<bool> GetBoolValue(string id) { ... do stuff ... }

Ответ 1

Принципы работы интерфейсов заключаются в том, что любой объект может реализовать IList<T> (если только он не известен как экземпляр закрытого типа, который не реализует его, я думаю) - поэтому всегда существует возможное обращение ссылочного типа к интерфейсу.

В последнем случае компилятор не желает этого делать, потому что он не знает, что T является bool, несмотря на предыдущий оператор if, поэтому он не знает, какое преобразование можно попробовать между MyClass<T> и MyClass<bool>. Допустимые преобразования в общие типы довольно ограничены, к сожалению.

Вы можете исправить это довольно легко:

return (MyClass<T>)(object) GetBoolValue(id);

Это некрасиво, но оно должно работать... и по крайней мере в этом случае он не будет вызывать никакого бокса.

Ответ 2

С# 4.0 позволяет объявлять ковариацию и контравариантность в параметризованных интерфейсах и делегировать типы.

Ответ 3

Что произойдет, если вы замените

return (MyClass<T>)

с

return (MyClass<bool>)