views:

483

answers:

6

What I'd like to do is something like the following:

FooClass.prototype.method = function():String
{
    return "Something";
}

var foo:FooClass = new FooClass();
foo.method();

Which is to say, I'd like to extend a generated class with a single method, not via inheritance but via the prototype.

The class is generated from a WSDL, it's not a dynamic class, and I don't want to touch the generated code because it will be overwritten anyway.

Long story short, I'd like to have the moral equivalent of C# 3:s Extension Methods for AS3.

Edit: I accepted aib's answer, because it fits what I was asking best -- although upon further reflection it doesn't really solve my problem, but that's my fault for asking the wrong question. :) Also, upmods for the good suggestions.

+2  A: 

Yes, such a thing is possible.

In fact, your example is very close to the solution.

Try

foo["method"]();

instead of

foo.method();
aib
+1  A: 

@aib is unfortunately incorrect. Assuming strict mode (the default compiler mode) it is not possible to modify the prototype of non-dynamic class types in ActionScript 3. I'm not even sure that it's possible in non-strict mode.

Is wrapping an option? Basically you create a class that takes one of the objects you get from the web service and just forwards all method calls to that, but also has methods of its own:

public class FooWrapper extends Foo {

    private var wrappedFoo : Foo;

    public function FooWrapper( foo : Foo ) {
        wrappedFoo = foo;
    }

    override public function methodFromFoo( ) : void {
     wrappedFoo.methodFromFoo();
 }

 override public function anotherMethodFromFoo( ) : void {
  wrappedFoo.anotherMethodFromFoo();
 }

 public function newMethodNotOnFoo( ) : String {
  return "Hello world!"
 }

}

When you want to work with a Foo, but also have the extra method you need you wrap the Foo instance in a FooWrapper and work with that object instead.

It's not the most convenient solution, there's a lot of typing and if the generated code changes you have to change the FooWrapper class by hand, but unless you can modify the generated code either to include the method you want or to make the class dynamic I don't see how it can be done.

Another solution is to add a step to your build process that modifies the source of the generated classes. I assume that you already have a step that generates the code from a WSDL, so what you could do is to add a step after that that inserts the methods you need.

Theo
+2  A: 

@Theo: How would you explain the following working in 3.0.0.477 with the default flex-config.xml (<strict>true</strict>) and even a -compiler.strict parameter passed to mxmlc?

Foo.as:

package
{
    public class Foo
    {
     public var foo:String;

     public function Foo()
     {
      foo = "foo!";
     }
    }
}

footest.as:

package
{
    import flash.display.Sprite;

    public class footest extends Sprite
    {
     public function footest()
     {
      Foo.prototype.method = function():String
      {
       return "Something";
      }

      var foo:Foo = new Foo();
      trace(foo["method"]());
     }
    }
}

Note that the OP said inheritance was unacceptable, as was modifying the generated code. (If that weren't the case, adding "dynamic" to the class definition would probably be the easiest solution.)

aib
+1  A: 

Depending on how many methods your class has, this may work:

Actual Class:

public class SampleClass
{
    public function SampleClass()
    {
    }

    public function method1():void {
     Alert.show("Hi");
    }

Quick Wrapper:

var actualClass:SampleClass = new SampleClass();

var QuickWrapper:Object = {
    ref: actualClass,
    method1: function():void {
     this.ref.method1();
    },
    method2: function():void {
     Alert.show("Hello!");
    } 
};

QuickWrapper.method1();
QuickWrapper.method2();
maclema
A: 

@aib is right, I made a mistake when I tested it out. I forgot that the prototype is only accessible on the class and not on each object, so for me I got runtime errors.

However, my wrapper solution isn't subclassing, it's wrapping. The wrapper class extends the original only so that it will have the same type.

Theo
A: 

Monkey patching is an (inelegant) option.

For example, suppose you don't like the fact that Flex 3 SpriteAsset.as returns a default border metrics of [7,7,7,7] (unlike flex 2). To fix this, you can:

  1. Create a copy of SpriteAsset.as and add it to your project at /mx/core/SpriteAsset.as
  2. Edit your local copy to fix any problems you find
  3. Run your ap

Google "flex monkey patch" for more examples and instructions.

Brian