Trap de interfaz de Delphi
Ahora conozco dos grandes dificultades:
Trampa 1. Tipo de interfaz Trap de conversión
a) No puede lanzar una referencia de objeto a una interfaz que no declare el tipo de esta referencia, incluso si el objeto realmente implementa esta interfaz (jeje, las ventajas son difíciles de pronunciar). b) Cuando se asigna una variable de objeto a una variable de interfaz y cuando la variable de interfaz se asigna a la variable de objeto, la dirección de la variable de objeto ha cambiado, es decir, ya no es el objeto original, sino que apunta a una dirección de error . Por ejemplo: i1 = interfaz
función do: boolean;
fin;
TC1 = clase
ATT1: entero;
fin;
Tc2 = class (TC1, I1)
ATT2: entero;
función do: boolean;
end; intf1: i1; obj1: tc !; obj2: tc2; obj2: = tc2.create;
Obj1: = obj2.
I1 (obj2) .do correcto.
I1 (OBJ1). Dado que el tipo TC1 de OBJ1 no declara que I1 se implementa, no se puede convertir a I1, incluso si OBJ1 implementa I1. Además, si convierte el objeto en una interfaz y luego regresa, habrá problemas. Obj2: = tc2.create; obj2.att1: = 0;
Intf1: = obj2; // correcto. Obj2: = intf1; Obj2.att1: = 0; Es decir, después de convertir de la referencia del objeto a la referencia del puntero, la dirección cambia, pero cuando la referencia del puntero se retira de la referencia del objeto y luego se devuelve a la referencia del objeto (¿el error de Delphi?).
TRAP 2. Interface Lifetime Management
Según mi sentido común (aquí hay un sentido común de programación, no el sentido común de uso de Delphi), creo que las interfaces no requieren una gestión de por vida, porque las interfaces no pueden generar objetos reales en absoluto. Pero Delphi una vez más llegó a mi sentido común (¿por qué dices "Renovar"?), Su interfaz tiene toda la vida y debe implementar los siguientes tres métodos: Function QueryInterface (const iID: tguid; out obj): hResult; stdcall; Así que no sé cómo implementar estos tres métodos. J Si no desea implementar estos tres métodos usted mismo, puede usar TComponent. Debido a que TComponent ha implementado estos tres métodos, se puede heredar de él, por lo que no es necesario implementar estos tres métodos. ¿Puedes usarlo con confianza? La respuesta es no. Porque Delphi en secreto (porque estaba más allá de mis expectativas) cuando establece la variable de interfaz en nil. Función _intfclear (Var Dest: Iinterface): Pointer; Var P: Pointer; ) ._Release; función tcomponent._release: integer; , entonces nada no se lo hizo. ¿Qué no estamos trabajando como objetos COM, por lo que no hay problema? La respuesta sigue siendo no, considere la siguiente situación: obj2: = tc2.create; tyintf1: = obj2; intf1.do finalmente obj2.free; Se producirá un error de acceso a la dirección ilegal. ¿Por qué? Como se mencionó anteriormente, cuando la referencia de la interfaz se establece en NIL, se llamará _intfclear, y _intfclear llamará a la liberación del objeto. ¿Algunas personas dicen que es innecesaria? Obj2: = tc2.create; tyintf1: = obj2; intf1.do; ¿Por qué? Debido a que el compilador de Delphi es inteligente, cree que olvidó establecer esta referencia de dirección a NIL, por lo que se la agregará automáticamente. ¿Cómo resolverlo? Método 1: Establezca la referencia de interfaz a NIL primero y luego suelte el objeto. Intf1: = nil; Pointer (intf1): = nil; Tiendo a usar el segundo método para que no tenga que pensar en quién lanzar primero. Además, en algunos patrones de diseño, solo puede tener referencias de interfaz, y no sabe cuándo se lanzará el objeto referenciado. Por ejemplo, considere el patrón compuesto. TComposite = Class (TComponent, I1) Interlist privado: TXContainer; // Una clase de contenedores que almacena la referencia de interfaz para "Leaf". Procedimiento público Añadir (Aintf: I1); Obviamente no, ¿entonces las "hojas" definitivamente se lanzarán más tarde que este objeto "objeto de síntesis"? No me parece. Si esta regulación es obligatoria, se perderá mucha flexibilidad. Por lo tanto, definitivamente pensamos que cuando estas referencias de interfaz se colocan nulos, no habrá relación con el objeto original, para evitar errores de acceso de dirección ilegal después de que se libere el objeto. ¿Qué contenedor debe considerar usar? ¿formación? Tlist? Tinterfacelist? En primer lugar, creo que debe ser una tinterfacelist, porque lo que queremos acomodar es la interfaz. Pero cuando es libre de él, pone todas las interfaces que acomoda a Nil, que es exactamente lo que no queremos. O podemos convertir la referencia de interfaz que almacena en un puntero antes de libre y luego configurarlo en nulo. para i: = 0 a interlist.count -1 dopointer (interlist.items [i]): = nil; Luego probamos la matriz. Interlist: matriz de I1; matrices dinámicas no deben liberarse. Parece ser muy útil, pero cuando el compilador lo libera, aún establecerá cada elemento en nulo, y como interfaz, todavía existe la posibilidad de errores de acceso de dirección ilegal. Se puede hacer de esta manera para i: = bajo (arr) a alto (arr) dopointer (arr [i]): = nil; El tiempo que lo usa, y es posible que no recuerde hacerlo o no. Finalmente, usa tlist. Sin embargo, hay un puntero en tlist, por lo que cuando se agrega, debe proceder de la siguiente manera: xxx.add (aintf: i1) comienza interlist.add (puntero (aintf)); fin, dado que originalmente guarda un puntero, no hay Necesidad de procesamiento especial al soltar. Parece ser perfecto, pero todavía hay una trampa. Interlist.Add (TC2.Create); Debido a que es un puntero puro, cuando se convierte en una interfaz, no se lleva a cabo la conversión de dirección del objeto a la referencia de la interfaz (no sabe cómo hacerlo), por lo que cuando llame al método declarado por la interfaz, Es otro error de acceso de dirección ilegal. Esta es la única forma de escribir: interlist.add (puntero (i1 (tc2.create)); aunque es un poco problemático, esta es la mejor solución (como sé). Porque si olvida que la transferencia comete un error en la primera llamada, es más fácil encontrar errores (en comparación con el uso de matriz). Entonces sugiero: 1. Use TList para administrar las referencias de interfaz. 2. Al agregar, debe transformar el objeto en una interfaz y luego en un puntero. 3. Para las referencias de interfaz que no se administran usando TLIST. Para referencia a las interfaces, debe establecerse manualmente en nulo por el siguiente método: Pointer (Intfref): = nil; Por lo tanto, heredar de él también puede ahorrarse el problema de implementar estos tres métodos. Pero su _relase se implementa así: función tinterfacedObject._release: Integer; El objeto. Así es como se implementa, trae más problemas que heredar de TComponent. A menos que se use lo contrario, no se recomienda. Todas las discusiones sobre las interfaces anteriores se limitan a las interfaces a nivel de lenguaje utilizadas en general, y no hay discusión sobre las interfaces COM+. La implementación de estas extrañas interfaces de Delphi tiene mucho que ver con el desarrollo de la interfaz COM+, que es el resultado del compromiso hecho debido a la pesada carga histórica.