2014-09-05 67 views
3

如果我们将一个对象变量赋值给pl/sql中的其他对象变量,则该对象将被克隆,因为pl/sql不适用于引用。例如,下面的代码将打印两个不同的句子:PL/SQL:避免克隆对象的技巧?

create or replace type cla as object -- class (would be very complex) 
(
    name varchar2(50) 
); 

declare 
    o1 cla; 
    o2 cla; 
begin 
    o1 := cla('hi cloning world'); 
    o2 := o1; 
    o1.name = 'goodbye cloning world'; 

    dbms_output.put_line('o1.name: ' || o1.name); 
    dbms_output.put_line('o2.name: ' || o2.name); 
end; 

所以,我以为我可以封装对象到其他对象(外部对象),如果我给你的外部对象到其他外部对象,则内部对象不会被克隆:

create or replace type cla as object -- class (would be very complex) 
(
    name varchar2(10) 
); 

create or replace type eo_c as object -- class to encapsulate objects 
(
    o cla        -- encapsulation is easy 
); 

declare 
    eo eo_c; 
    eo2 eo_c; 
begin 
    eo := eo_c(cla('n1'));   -- eo is easy to create 
    dbms_output.put_line('eo.name: ' || eo.o.name); -- real object is easy to access 

    eo2 := eo;  -- eo_c object is cloned, cla object shouldn't be cloned 
    eo.o.name := 'n2';  -- if eo.o == eo2.o then we are changing both names 
    dbms_output.put_line('eo.name: ' || eo.o.name); 
    dbms_output.put_line('eo2 name: ' || eo2.o.name); 
end; 

但是,再次打印两个不同的句子,所以内部对象也被克隆了。

我可以将对象封装到其他类型的变量中以避免克隆内部对象吗?或者更一般地说,是否有一些技巧可以避免克隆对象,同时允许使用它的简单方法?

+2

不知道你能避免PLS-00536与'ref'在PL/SQL上下文中,您在第一句中暗示了这一点。为什么你想分配相同的对象到两个变量?我想你可以有一个关联的对象数组,并让你的变量将索引保存到那些对象中,但是重定向可能会很痛苦。 – 2014-09-05 15:33:12

+0

为了便于编程,需要参考副本。例如,如果通过将其他对象传递给构造函数“o_c1:= c1(o_c2,o_c3);”来创建对象,则o_c2和o_c3可以作为属性存储到o_c1中,并且它们应该与您正在使用(和更改)o_c1的对象,而不是它们的克隆。强大的oo编程需要对象引用。 – freesoft 2014-09-08 06:51:32

回答

1

基于亚历克斯建议(使用关联数组),我创建了一个封装对象的封装,因此我们可以以一种抽象的方式使用它们,好像它们是引用:

create or replace type cla as object  -- complex class 
(
    name varchar2(10) 
); 


create or replace package eo as  -- package to encapsulate objects 
    type ao_t      -- type for hash (associative array) 
     is table of cla 
     index by varchar2(100); 
    o ao_t;       -- hash of objects 
end; 


declare 
    o1 varchar2(100); 
    o2 varchar2(100); 
begin 
    o1 := 'o1';       -- objects are hash indexes now 
    eo.o(o1) := new cla('hi');   -- store new object into the hash 
    o2 := o1;       -- assign object == assign index 
    eo.o(o1).name := 'bye';    -- change object attribute 

    dbms_output.put_line('eo.o(o1).name: ' || eo.o(o1).name); 
    dbms_output.put_line('eo.o(o2).name: ' || eo.o(o2).name); -- equal? 
end; 

现在“再见'如预期的那样用对象引用书写两次。关键是o1和o2都包含相同的索引(〜引用)到同一个对象。语法稍微复杂一些,但在访问属性和方法时仍然非常类似于标准对象操作。

分配一个目的是其他是完全一样的标准对象分配:

o2 := o1; 

同样为使用对象作为函数参数:

afunc(o1); 

在内部,afunc()将只使用O1与访问方法或属性的相同特殊语法(以及不需要指定的特殊语法):

eo.o(o1).attrib := 5; 
eo.o(o1).method('nice'); 
o3 := o1; 

使用这个技巧的唯一要求是为我们要封装的每个类添加一个散列(类型和变量)到eo包。


更新

o1 := 'o1'; 

可能是一个问题,如果,例如,我们创建一个函数,对象,因为该功能还必须是:基于变量名的索引值知道程序其余部分使用的所有值,以避免重复值。一种解决方案是从散列尺寸取值:

o1 := eo.o.count; 

这需要我们成其他问题:散列含量persitent(因为它是成一个包),所以越来越多的对象将被添加到该散列作为我们创建对象(即使对象是由相同的函数创建的)。一个解决办法是从哈希删除对象,当我们这个对象:

eo.o(o1) = null; 

所以固定程序是:

create or replace type cla as object  -- complex class 
(
    name varchar2(10) 
); 


create or replace package eo as  -- package to encapsulate objects 
    type ao_t      -- type for hash (associative array) 
     is table of cla 
     index by varchar2(100); 
    o ao_t;       -- hash of objects 
end; 


declare 
    o1 varchar2(100); 
    o2 varchar2(100); 
begin 
    o1 := eo.o.count;     -- index based on hash size 
    eo.o(o1) := new cla('hi');   -- store new object into the hash 
    o2 := o1;       -- assign object == assign index 
    eo.o(o1).name := 'bye';    -- change object attribute 

    dbms_output.put_line('eo.o(o1).name: ' || eo.o(o1).name); 
    dbms_output.put_line('eo.o(o2).name: ' || eo.o(o2).name); -- equal? 

    eo.o(o1) = null;     -- remove object 
    eo.o(o2) = null;     -- remove object (redundant) 
end;