我在編寫一系列相互繼承的類別時遇到了問題。
為了簡單起見,我將程式碼剝離為下面的基本範例。
Basically i have a series of classes inheriting from each other in cascade (called MyBaseClass, MyDerivedClass, MyDoubleDerived)and they differ for the value assigned to the F_Type property (value 1 2 and 3 respectively). i am creating an instance of MyDoubleDerived Class assigning it to a variable (and also storing into an Associative array where i can get to it by its ID).
我發現在實例化期間以及當我嘗試從物件本身存取該屬性時,F_Type 被正確地設定為 3。
但是,當我在方法內部時, this:F_Type 或 F_Type 的值由於某種原因為 2(但如果我從儲存物件的關聯數組訪問,則該值正確為 3)。奇怪的是,該方法是在基類上定義的(該基類的F_Type= 1,所以我無法理解為什麼該值為2。這可能是某種類型的錯誤嗎?(我在JMP 17.1.0 和JMP 18 Early 中都看到了這一點)發布,我沒有早期版本可供測試)
If(!Namespace Exists( "MyNameSp" ),
NS=New Namespace( "MyNameSp" );
);
MyNameSp:ARList=associative array(); // i store here the object that i can access via its key for convenience
<BR />// below the fucntion to instantiate the object from the class
MyNameSp:CreateNewObj=Function({ClassName, NewKey},
{Default Local},
ObjRef=NewObject(ClassName,{NewKey});
show(ObjRef:ObjID);
MyNameSp:ARList[ObjRef:ObjID]=ObjRef;
return(ObjRef);
);
Define Class( "MyBaseClass",
F_Type=1;
ObjID="";
_init_=Method({NewKey},
this:ObjID=NewKey;
show("value during initialization");
show(this:F_Type);
show(F_Type);
);
MyMethod=Method({},
show("value in method");
show(F_Type);
show(this:F_Type);
show(this:ObjID);
show(MyNameSp:ARList[this:ObjID]:F_Type);
);
);
Define Class( "MyDerivedClass",
baseclass( MyBaseClass ),
F_Type=2;
);
////////////////////////////////////////////////////////////////////////////
Define Class( "MyDoubleDerived",
baseclass( MyDerivedClass ),
F_Type=3;
);
<BR />// start of the actual script
SampleObj=MyNameSp:CreateNewObj("MyDoubleDerived","Key1");
show("value From script");
show(SampleObj:F_Type);
SampleObj:MyMethod();
我仔細檢查了方法,同樣的行為也適用。
如果您從另一個方法呼叫特定方法,並且「目標」方法在不同的繼承層級上有所不同,則使用堆疊中「上一級」類別的方法(不確定措辭是否有意義)。
類似地,如果我從「_init_」方法定義衍生類別的方法,所有這些都會按預期工作
雖然對於變量,我可以理解“_init_”方法中執行的邏輯,但這些方法似乎違反直覺。
有相關文件嗎?
這是修改後的範例:
If(!Namespace Exists( "MyNameSp" ),
NS=New Namespace( "MyNameSp" );
);
MyNameSp:ARList=associative array();
MyNameSp:CreateNewObj=Function({ClassName, NewKey},
{Default Local},
ObjRef=NewObject(ClassName,{NewKey});
show(ObjRef:ObjID);
MyNameSp:ARList[ObjRef:ObjID]=ObjRef;
return(ObjRef);
);
Define Class( "MyBaseClass",
F_Type=1;
K_Type=1;
ObjID="";
_init_=Method({NewKey},
this:ObjID=NewKey;
);
MyChangeInitMethod=Method({PassedType, passedID},
show("INIT Base Level method (1) ",PassedType, passedID);
);
MyMethod=Method({},
this:MyChangeMethod(this:F_Type,this:ObjID);
this:MyChangeInitMethod(this:F_Type,this:ObjID);
);
MyChangeMethod=Method({PassedType, passedID},
show("Base Level method (1) ",PassedType, passedID);
);
);
Define Class( "MyDerivedClass",
baseclass( MyBaseClass ),
F_Type=2;
_init_=Method({NewKey},
this:ObjID=NewKey;
K_Type=2;
MyChangeInitMethod=Method({PassedType, passedID},
show("INIT Derived class Method (2)",PassedType, passedID);
);
);
MyChangeMethod=Method({PassedType, passedID},
show("Derived class Method (2)",PassedType, passedID);
);
);
////////////////////////////////////////////////////////////////////////////
Define Class( "MyDoubleDerived",
baseclass( MyDerivedClass ),
F_Type=3;
_init_=Method({NewKey},
this:ObjID=NewKey;
K_Type=3;
MyChangeInitMethod=Method({PassedType, passedID},
show("INIT double Derived class method (3)",PassedType, passedID);
);
);
MyChangeMethod=Method({PassedType, passedID},
show("double Derived class method (3)",PassedType, passedID);
);
);
////////////////////////////////////////////////////////////////////////////
Define Class( "MyTripleDerived",
baseclass( MyDoubleDerived ),
F_Type=4;
_init_=Method({NewKey},
this:ObjID=NewKey;
K_Type=4;
MyChangeInitMethod=Method({PassedType, passedID},
show("INIT Triple Derived class method (4)",PassedType, passedID);
);
);
MyChangeMethod=Method({PassedType, passedID},
show("Triple Derived class method (4)",PassedType, passedID);
);
);
SampleObj1=MyNameSp:CreateNewObj("MyBaseClass","Key1");
SampleObj2=MyNameSp:CreateNewObj("MyDerivedClass","Key2");
SampleObj3=MyNameSp:CreateNewObj("MyDoubleDerived","Key3");
SampleObj4=MyNameSp:CreateNewObj("MyTripleDerived","Key4");
show("----------------Calling the method via a method-------------------");
show(" 1 ");
SampleObj1:MyMethod();
show(" 2 ");
SampleObj2:MyMethod();
show(" 3 ");
SampleObj3:MyMethod();
show(" 4 ");
SampleObj4:MyMethod();
show("----------------Clling the method directly------------------------");
show(" 1 ");
SampleObj1:MyChangeMethod(SampleObj1:F_Type,SampleObj1:ObjID);
show(" 2 ");
SampleObj2:MyChangeMethod(SampleObj2:F_Type,SampleObj2:ObjID);
show(" 3 ");
SampleObj3:MyChangeMethod(SampleObj3:F_Type,SampleObj3:ObjID);
show(" 4 ");
SampleObj4:MyChangeMethod(SampleObj4:F_Type,SampleObj4:ObjID);</CODE></P><P> </P></LINGO-BODY><LINGO-SUB id="lingo-sub-725797" slang="en-US" mode="CREATE">回覆:類別繼承,我缺少什麼?</LINGO-SUB><LINGO-BODY id="lingo-body-725797" slang="en-US" mode="CREATE"><P>謝謝回覆。</P><P>雖然重新調整程式碼很容易,我可以繼續工作,但我並沒有真正理解幕後的內容。</P><P>當我直接調用屬性時,它會得到正確的值(3),只有在方法內部,該值才為 2(而不是 1),我還嘗試添加另一個級別(並且始終是 n-1 級別)被認為)。</P><P> </P><P>此外,這引發了另一個問題,關於也儲存在變數中的方法的繼承如何......我是否還應該從「_init_」定義特定方法?</P><P> </P><P> </P></LINGO-BODY><LINGO-SUB id="lingo-sub-725714" slang="en-US" mode="CREATE">回覆:類別繼承,我缺少什麼?</LINGO-SUB><LINGO-BODY id="lingo-body-725714" slang="en-US" mode="CREATE"><P>這可能更接近你想要的</P>
<PRE><CODE class=" language-jsl">If(!Namespace Exists( "MyNameSp" ),
NS=New Namespace( "MyNameSp" );
);
MyNameSp:ARList=associative array(); // i store here the object that i can access via its key for convenience
// below the fucntion to instantiate the object from the class
MyNameSp:CreateNewObj=Function({ClassName, NewKey},
{Default Local},
ObjRef=NewObject(ClassName,{NewKey});
show(ObjRef:ObjID);
MyNameSp:ARList[ObjRef:ObjID]=ObjRef;
return(ObjRef);
);
Define Class( "MyBaseClass",
F_Type=1;
ObjID="";
_init_=Method({NewKey},
this:ObjID=NewKey;
show("value during initialization");
show(this:F_Type);
show(F_Type);
);
MyMethod=Method({},
show("value in method");
show(F_Type);
show(this:F_Type);
show(this:ObjID);
show(MyNameSp:ARList[this:ObjID]:F_Type);
);
);
Define Class( "MyDerivedClass",
baseclass( MyBaseClass ),
// F_Type=2;
_init_=Method({NewKey},
super:_init_(NewKey);
F_Type=2;
show("MyDerivedClass",F_Type);
);
);
////////////////////////////////////////////////////////////////////////////
Define Class( "MyDoubleDerived",
baseclass( MyDerivedClass ),
// F_Type=3;
_init_=Method({NewKey},
super:_init_(NewKey);
F_Type=3;
show("MyDoubleDerived",F_Type);
);
);
// start of the actual script
SampleObj=MyNameSp:CreateNewObj("MyDoubleDerived","Key1");
show("value From script");
show(SampleObj:F_Type);
SampleObj:MyMethod();
"value during initialization";
this:F_Type = 1;
F_Type = 1;
// above, the base class is constructed (at beginning of intermediate)
"MyDerivedClass";
F_Type = 2;
// above, the intermediate class is constructed (at the beginning of most derived)
"MyDoubleDerived";
F_Type = 3;
// above, the most derived class is constructed
ObjRef:ObjID = "Key1";
"value From script";
// below: there is really only one variable F_Type, belonging to the base class
SampleObj:F_Type = 3;
"value in method";
F_Type = 3;
this:F_Type = 3;
this:ObjID = "Key1";
MyNameSp:ARList[this:ObjID]:F_Type = 3;
衍生類別使用 super:_init_ 確保其基底類別已完全構造,然後繼續初始化派生類別。在每一層重新定義F_Type是隱藏基底類別F_Type,而不是取代它。我不明白為什麼你的範例在方法呼叫中顯示 F_Type=2 而不是 F_Type=1,但問題會隨著上面的使用模式而消失。
克雷格,
再次感謝您的快速回覆。
我玩得越多,我就越相信我原來的變數和方法的「覆蓋」應該是它應該的方式,並且當前的行為應該被修改。
不幸的是,我意識到在 _init_ 中添加方法對於「多層」繼承並不是真正有用的解決方案。
如果一個方法應該在「中間」類別中自然地描述,則較低層級將無法存取該方法,因為添加到_init_ 只會將其設定為我建立的活動對象,但不會充當從中派生的類別的原型。
同樣,屬性的行為也應該如此。我認為應該能夠在不需要 _Init_ 的情況下聲明它們。只有當該值在運行時設置且未預定義時才應使用_Init_ (我認為),在_Init_ 中定義將要求我為所有派生類指定它,而不僅僅是在我需要更多維護工作的級別上指定它
讓我們以形狀為例。我有一個通用多邊形,您需要在運行時指定“邊”作為輸入,因此我會將其放在輸入中並在物件啟動時定義。我希望對於四邊形,我會將“邊”硬編碼為 4。但是對於正方形,我希望繼承四邊形,而不是再次將其定義為 4。
側面很好......但如果我需要初始化一個長列表或關聯數組,則為每個類別重複它會很煩人。
你有什麼意見?
我曾多次嘗試使用這些類,並發現了一些很好的用例(透過引用傳遞)、一些錯誤和一些缺失的功能(這個)。
我仍在完善我的模型的工作原理,感謝您的問題和討論。
- 我認為將每個子類別視為具有自己的類似命名空間的成員變數集以及指向下一個基類成員變數集的隱藏連結將有所幫助。並且,將其視為該名稱空間。您希望這是最衍生的類,因為從那裡您可以點擊最基礎類的連結並學習所有內容。有時可能會出現問題,或至少與預期相反,當呼叫方法時,這會成為該方法所在的命名空間。沒有返回子類別的連結。沒關係,除非該方法期望呼叫子類別中的虛擬函數。因此,如果子類別使用 _init_ 來取代基底類別中的虛擬函數(成功)或將其分配到虛擬函數的命名空間(失敗),則會產生影響。
- 我非常確定正在進行的複製比您希望的要多,並且這樣做是為了使多層繼承發揮作用。建立 3 級類時,先複製基類,然後複製中間類,最後複製最衍生類。我相信嵌套的 _init_ 呼叫在那之後發生。