¿Por qué los generics C # no pueden derivarse de uno de los parámetros de tipo genérico como lo hacen en las plantillas C ++?

¿Por qué los generics C # no pueden derivarse de uno de los parámetros de tipo genérico como lo hacen en las plantillas C ++? Quiero decir que sé que es imposible porque CLR no es compatible con esto, pero ¿por qué?

Soy consciente de las profundas diferencias entre las plantillas de C ++ y los generics de C #: las primeras son entidades de tiempo de comstackción y deben resolverse durante la comstackción, mientras que las últimas son entidades de tiempo de ejecución de primera clase.

Aún así, no veo la razón por la cual los diseñadores de CLR no llegaron a un esquema que finalmente permitiera que un tipo genérico de CLR derivara de uno de sus parámetros de tipo genérico. Después de todo, esta sería una característica tremendamente útil, personalmente la extraño mucho.

EDITAR:

Me gustaría saber si hay algún problema importante que se solucione y que genere un precio tan alto en la implementación de esta función que justifique que aún no se haya implementado. Por ejemplo, examine esta statement ficticia:

class C : T { } 

Como Eric Lippert ha notado qué pasa si ” ¿Qué pasa si T es una estructura? ¿Qué pasa si T es un tipo de clase sellado? ¿Qué pasa si T es un tipo de interfaz? ¿Qué pasa si T es C? ¿Qué pasa si T es una clase derivida de C? si T es un tipo abstracto con un método abstracto? ¿Qué pasa si T tiene menos accesibilidad que C? ¿Qué pasa si T es System.ValueType? (¿Se puede tener un non-struct que hereda de System.ValueType?) ¿Qué pasa con System.Delegate, System.Enum, y así sucesivamente?

Como Eric continúa, ” Esas son las fáciles, obvias “. De hecho, él tiene razón. Estoy interesado en un ejemplo concreto de algún problema, ni fácil ni obvio, que es difícil de resolver.

Bueno, comienza por preguntarte qué podría salir mal con la class C : T { } . Una gran cantidad de cosas vienen inmediatamente a la mente:

¿Qué pasa si T es una estructura? ¿Qué pasa si T es un tipo de clase sellado? ¿Qué pasa si T es un tipo de interfaz? ¿Qué pasa si T es C ? ¿Qué pasa si T es una clase derivada de C ? ¿Qué pasa si T es un tipo abstracto con un método abstracto? ¿Qué pasa si T tiene menos accesibilidad que C? ¿Qué pasa si T es System.ValueType? (¿Puede tener una estructura que no hereda de System.ValueType?) ¿Qué pasa con System.Delegate, System.Enum, etc.?

Esos son los fáciles, obvios. La característica propuesta abre literalmente cientos, si no miles, de preguntas más sutiles sobre la interacción entre el tipo y su tipo base, todo lo cual tendría que ser cuidadosamente especificado, implementado y probado. Sin duda, perderemos algunos, y por lo tanto ocasionaremos cambios bruscos en el futuro, o montaremos el tiempo de ejecución con un comportamiento definido por la implementación.

Los costos serían enormes, por lo que el beneficio debería ser enorme. No veo un beneficio enorme aquí.

De acuerdo, si no le gustó mi respuesta anterior, tomemos un rumbo diferente.

Su pregunta presupone una falsedad: que necesitamos una razón para no implementar una característica. Por el contrario, necesitamos una razón muy, muy buena para implementar cualquier función. Las características son enormemente costosas en sus costos iniciales, en sus costos de mantenimiento y en los costos de oportunidad. (Es decir, el tiempo que pasa en la característica X es el tiempo que no puede gastar en la función Y, y que puede impedirle usar la característica Z). Para poder entregar valor de manera responsable a nuestros clientes y partes interesadas, no podemos implementar cada función que a alguien le gusta.

No corresponde a los diseñadores de tiempo de ejecución justificar por qué no implementaron una función que le parece particularmente agradable. Las características se priorizan en función de sus costos frente al beneficio para los usuarios, y los usuarios no han estado exactamente presionando mi puerta exigiendo este tipo de herencia. Esta característica particular cambiaría masivamente la forma en que el análisis del sistema de tipos funciona en el tiempo de ejecución, tiene efectos de gran scope en todos los idiomas que consumen generics, y me parece que proporciona muy poco beneficio.

Usamos este tipo de herencia en el comstackdor, escrito en C ++, y el código resultante es difícil de seguir, difícil de mantener y confuso de depuración. He estado haciendo todo lo posible para eliminar código de esta manera. Me opongo a habilitar el mismo tipo de patrones malos en C # a menos que haya un beneficio enormemente convincente para hacerlo.

La tarea de describir ese enorme beneficio de una manera convincente recae sobre las personas que quieren la característica, no sobre las personas que deberían implementarla. Entonces, ¿cuál es el beneficio convincente?

Ejemplo de código, donde esto podría ayudar:

 public class SpecialDataRow : T where T : DataRow { public int SpecialFactor { get; set; } } 

Esto permitiría hacer filas ‘especiales’ desde DataRow y también desde cualquier DataRows derivado (como las generadas por conjuntos de datos tipeados)

No veo otra forma de codificar esa clase

¿Qué sería tan útil acerca de esto?

Recuerde que, a pesar del nombre, los generics nunca tuvieron la intención de respaldar la progtwigción genérica.

Para admitir una característica como esta, tendrían que hacer algunos cambios bastante dramáticos en la CLR.

Debería definir una clase que se derive de un tipo que ni siquiera existe en tiempo de comstackción.

¿Por qué deberían saltar a través de tales aros y comprometer bastante fundamentalmente su sistema de tipos solo para agregar esta característica? ¿Vale la pena?

Si lo crees, diles por qué. Escriba comentarios en connect.microsoft.com diciéndoles por qué esta función es tan fundamental que debe agregarse.

Las plantillas C ++ no se pueden comparar con los generics C #. Las plantillas de C ++ se procesan previamente como macros, mientras que los generics en .NET son manejados por el tiempo de ejecución.

Pero hay otras personas que saben mucho más sobre eso que yo …