简体   繁体   中英

Call function problems as3

I'm calling a function from a child class and passing the current section variables for it to then load/remove the next section, everything works fine until I then try to pass the same variable to core function I used previously. I can't understand why it's not working again.

Problem with the following :

1180: Call to a possibly undefined method setupSection.

  • Initially I setup the parameters and to load the class from a standard function :

     public function setupVariables():void { //Main code here //Establish Section currentSection = secData; setupSection(currentSection); } public function setupSection(sectionHolder:String):void { //Here selects the right choice from an Array and loads new Section. } //I call the function from the child Main.changeSection("loadNext"); public static function changeSection(lSection:String):void { var lSection : String; currentSection = lSection; trace("Val Passed = " +currentSection); //TraceOutput is - Val Passed = loadNext setupSection(currentSection); } public function setupSection(sectionHolder:String):void { //LoadSection etc. . . } 

The offending command appears to be this :

setupSection(currentSection);

currentSection is a String set up previously.

I've used this previously and it worked fine but not from the static function.

If any one can shed some light on why this isn't working I'd be so grateful as I just can't get it to work.

A static function cannot call an instance function. Static is essentially global and not associated with any instance of the class, so it can't call an instance function because it isn't an instance and has no idea what (if any) instances exist.

What you can do, if you really want to keep changeSection() as a static function, is to pass an instance of the class as an argument so that you can call setupSection() on the instance. Example:

public static function changeSection(lSection:String, main:Main):void { 
    var lSection : String; currentSection = lSection;

    trace("Val Passed = " +currentSection);
    //TraceOutput is - Val Passed = loadNext

    main.setupSection(currentSection);
}

And if you don't have easy access to an instance of the class when calling the function, what you are trying to do is more like a singleton . In this case you need to store a static reference to your single instance, so that you can easily access it from anywhere. Then you only need instance functions, and you call those through the static reference to the instance. For example:

class Main {
    public static var main:Main;
    public var currentSection:String;
    public function Main() {
        main = this;
    }
    public function changeSection(section:String):void {
        currentSection = section;
        setupSection(currentSection);
    }
    public function setupSection(section:String):void {
        // setup section
    }
}

Now you can use your Main class from anywhere like you currently are doing, like this:

Main.main.changeSection("loadNext");

I should mention, though, that is not generally considered a good design, because you are globally coupling your code to Main . This gets messy and harder to expand on, as you've already discovered in a small sense. A better solution is to use events or dependency injection .


Using events is a little more setup, but it's typically much cleaner and less problematic in the long run. I've never regretted refactoring deeply coupled code into events.

First, you can define a nice little custom event class that tells you a section should change (you don't always need a custom event class, you can use the generic Event class, but a custom class lets you define custom data for the event):

public class SectionEvent extends Event {
    public static var SECTION_CHANGE:String = "sectionChange";
    public var section:String;
    public function SectionEvent(type:String, section:String){
        super(type, true); // bubbles=true, important later
        this.section = section;
    }
}

Now you can listen for this event and handle it in your Main class (which I'm assuming is a document class):

class Main extends MovieClip {
    public function Main(){
        addEventListener(SectionEvent.SECTION_CHANGE, sectionChangeHandler);
    }
    private function sectionChangeHandler(event:SectionEvent):void {
        changeSection(event.section);
    }
    public function changeSection(section:String):void {
        currentSection = section;
        setupSection(section);
    }
    private function setupSection(section:String):void {
        // setup section
    }
}

Now to change the section, simply dispatch the event (from any child display object of Main , it will bubble up since we set bubbles=true) like this:

dispatchEvent(new SectionEvent(SectionEvent.SECTION_CHANGE, "nextSection"));

Hopefully you can see that even though this adds a little extra code, it organizes it much better. It makes Main look very approachable, describes the action through an event very clearly, and isn't hard to understand and use. Each part of the flow is a tightly encapsulated piece of code that you can understand, expand, and test easier than before.

As @Aaron pointed out in his answer (and my initial comment), static functions/properties are not associated with an instance of a class.

To explain this more thoroughly, I will give an example. Lets say we have a class called Main and a class called Section .

class Section extends Sprite{
    public Section(){}

    public function a():void{
        //this is a function of an instance
    }

    public static function b():void{
       //a static function of the class
       a();//this will not work
    }
}

class Main extends Sprite{
    public Main(){
        var first:Section = new Section();
        var second:Section = new Section();
    }
}

As you can see, our Main class creates two instances of our Section class. For every instance you have, space is cleared in your RAM for all your properties and functions. So both first and second each have their own little space in your RAM. Both of them also possess a function called a() , because non-static functions/properties are associated to their instance. But what about the function b() ? Well, this one exists indepent from any instance, and only exists once in your Ram.

So, when you try to do something like this

first.b();

I guess you assume that the function a() of the first instance is called. But this isn't possible because the static function doesn't know what a() you're referring to, it could be either first or second because b() exists outside them.

Hopefully my explanation is easy to understand :)


But lets actually solve your issue, without any static functions. As mentioned in one of my comments, you could (should?) use Events here. I will forego the Custom Event Shenanigans and will instead provide a more simple (but more hacky) solution.

In your setupSection() function, you probably do something like this

public function setupSection(sectionHolder:String):void{
    var s:Section = //you get the appropiate child instance somehow
    addChild(s);
}

What you need to do is to listen to Events that are dispatched from this instance, which would look like this:

var s:Section = //yadayada
s.addEventListener(Event.CHANGE, onSectionChange);
addChild(s);

In your Child class, when you want to change the Section, instead of calling a static function, you do this

var e:Event = new Event(Event.CHANGE);
e['sectionname'] = //the desired section
dispatchEvent(e);

Now, after the Event has been dispatched, the function onSectionChange in your Main instance will be called. The appropiate function would look like this

private function onSectionChange(e:Event):void{
  var sectionname:String = e['sectionname'];
  setupSection(sectionname);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM