2012-02-08 13 views
6

Sé que el título es muy amplio, abarcando mucho.Genéricos, polimorfismo, interfaces: ¿cuál es la solución?

Y espero que esta pregunta pueda evolucionar a una mayor "información wiki thingy" sobre los temas.

Lo que he aprendido - hasta ahora:

  • Al utilizar los genéricos - Comprender los conceptos (covariance and contravariance).
  • Do NOT "mis-use" el concepto de genéricos combinado con la herencia. ¡Lo hice y podría hacerte enfrentar directamente los problemas de covarianza! Asegúrese de "interrumpir" el genérico en el punto correcto de su herencia, si está combinando los dos.

(corríjame si crees que me equivoco, me falta algo o no he entendido nada).

Mi problema era:

Pero por ahora he pasar horas y horas, tratando de averiguar, cómo resolver este "gran rompecabezas" Tengo en mi escritorio. Y ya obtuve algunas buenas respuestas de varios de ustedes usuarios de SO, pero ahora es el momento de obtener algo que funcione en una escala mayor.

me aventuré en los genéricos con éste: Generics and Polymorphism working together

y ahora estoy un poco atascado en este caso: Situations where Generics won't work

Por qué termino con problemas de covarianza - es debido a mi clase de procedimiento mi jerarquía

Así que me pregunto si Interfaces es mi próximo movimiento audaz en esta "saga". ¿Cómo se puede "pasar" por un problema de covarianza? Una cosa es descubrir que realmente tiene este problema; otra cosa es "cómo solucionarlo".

Así que si cualquiera de ustedes, buena gente, "por ahí" tiene alguna opinión sobre esto, soy todo oídos. Básicamente: Dime que vaya por Interfaces (nunca he hecho uno desde cero). O .. arrojame un hueso en la dirección que sugieras.

Mi grupo de fuentes actual es como se indica en el segundo enlace, desde la parte superior.

Aquí hay un pequeño fragmento de mi anterior post que muestra mi problema de covarianza. David amablemente explicado - ¿Por qué me topé con el arbusto ... pero ahora necesito información sobre cómo manejarlo?

var  
    aList : TBaseList<TBaseObject>; // used as a list parameter for methods 
    aPersonList : TPersonList<TPerson>; 
    aCustomerList : TCustomerList<TCustomer>; 
begin 
    aPersonList := TPersonList<TPerson>.Create; 
    aCustomerList := TCustomerList<TCustomer>.Create; 

    aList := aCustomerList; <-- this FAILS !! types not equal .. 

end; 

Saludos

+1

Ayudaría si realmente supiéramos cuál es su problema real. En mi experiencia, la covarianza rara vez es un problema, si el diseño no es demasiado complejo. IOW, cuéntanos el problema real, entonces tenemos algo concreto que resolver. –

+0

@RudyVelthuis Bueno ... como usted menciona, es muy complejo y requeriría mucha reescritura del anterior [post] (http://stackoverflow.com/q/9140485/696574). Pero básicamente es el mismo problema: solo la pregunta ha cambiado. Como ahora sé lo que hice mal, simplemente espero que alguien me pueda orientar en la búsqueda de una solución. Si aún no está claro, házmelo saber y lo intentaré y explicaré lo mejor que pueda. –

+0

Cómo solucionar el problema depende de cuál es el problema * *. Una vez que asigna un valor a 'aList', ¿qué esperaba * hacer * con esa variable? (Además, ¿por qué son genéricos TPersonList y TCustomerList? ¿Qué * else * puede tener una TPersonList de?) –

Respuesta

5

No puede hacer lo que quiere hacer, pero esa no es la forma en que usa los genéricos de todos modos. Como dijo Rob Kennedy, no tiene sentido tener un TCustomerList<TCustomer> y un TPersonList<TPerson>. La belleza de los genéricos es que puedes usar la misma lista para diferentes tipos de elementos. Eso significa que la lista y el tipo de elemento no debe tener ninguna dependencia.

Usted puede hacer algo como:

procedure TSomething.ProcessList<T: TBaseObject>(const aList: TBaseList<T>); 
begin 
    // process the list using code that is independent of the actual type of T. 
end; 

... 

var 
    aCustomerList: TBaseList<TCustomer>; 
    aPersonList: TBaseList<TPerson>; 
begin 
    ProcessList(aCustomerList); 
    ProcessList(aPersonList); 

Tal vez puede que tenga que especificar T (algunas versiones anteriores de los genéricos no manejar la inferencia de tipos - es decir, que se infiere del tipo de T con el tipo de el parámetro - muy bien), es decir,

ProcessList<TCustomer>(aCustomerList); 
    ProcessList<TPerson>(aPersonList); 

Pero eso, o algo similar, es lo que debe hacer. Cualquier otra cosa no tiene sentido, IMO. No es necesario tener una variable que pueda contener cualquiera de estas listas, como su aList. Y si realmente lo necesita, solo puede usar TObject, pero eso no le permite usar la lista de ninguna manera útil. Y no es muy genérico.

Las interfaces no le ayudarán en absoluto con este problema. Puede dar a las clases ciertas capacidades, es decir, también los elementos de las listas, a través de interfaces (otro tipo de polimorfismo). Pero eso no manejará la covarianza.

+0

+1 La inferencia de tipo significa que debe poder escribirlo como 'Lista de proceso (aCustomerList)' y 'Lista de proceso (aPersonList)'. –

+0

@David: Lo sé, pero la inferencia de tipos no siempre funciona como se esperaba, especialmente no en las versiones anteriores que admitían genéricos. Observe que escribí ProcessList (etc.), pero hice la condición de que no se acepte de esa manera. –

+0

"Tal vez tenga que especificar" es un poco vago. Creo que eso es lo que me confundió. Es mejor que salgas y digas que la sintaxis concisa debería funcionar, pero a algunas versiones del compilador no les gusta.Curiosamente, hace poco controlé un error genérico que era al revés. Escriba la sintaxis de inferencia trabajada, la alternativa produjo un error interno. –

0

Yo iría por:

TCustomCustomerList = class(TBaseList<TBaseObject>) 
end; 

TCustomerList = class(TCustomCustomerList) 
end; 

Si es o no es aceptable en su diseño es una cuestión totalmente diferente. Si el objetivo que intenta lograr es asignar una TCustomerList a una variable TBaseList, ese sería el camino a seguir.

+0

Así que estás proponiendo que rompa la cadena genérica justo encima de la definición base ... hmmm ... Voy a tener una grieta en un proyecto de prueba y me pondré en contacto contigo. Podría tomar unas horas antes de que pueda volver a contactarlo. –

+0

Como puedes ver, actualmente estoy probando ... y hasta ahora parece que voy a seguir el consejo de Rudy (básicamente el mismo que el tuyo, pero explicado con más detalle). –

Cuestiones relacionadas