我在编写一系列相互继承的类时遇到了问题。
为了简单起见,我将代码剥离为下面的基本示例。
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_ 调用在那之后发生。