Domanda

Is there a simple, lightweight/inexpensive call to determine if an object supports a named method? So, in this the obj.respondsTo would be great.

dynamic _toJson(dynamic obj) {
  return obj.respondsTo('toJson'))? obj.toJson() : obj;
}

class Foo {
  String foo = "Foo.foo";
  Bar bar = new Bar();
  Map toJson() {
    return {
      "foo" : _toJson(foo),
      "bar" : _toJson(bar)
    };
  }
}

One alternative would be just call it and catch a noSuchMethod exception, but I imagine that is bad practice and expensive?

È stato utile?

Soluzione

The short answer is, 'no'. The answer provided by Frédéric Hamidi is not incorrect, but it does not work in dart2js (dart:mirrors is largely unimplemented in dart2js).

Also, while checking whether an object responds to a particular method is very common in other languages (Ruby, for example), it does not seem particularly Dart-y to me. Maybe once mirrors are fully supported in Dart, this will change.

And its hard to say whether reflection based on mirrors is 'lightweight/inexpensive'. It depends on the use case and how you define these terms.

I would say that your best bet is call the method on the object, catch the NoSuchMethod exception, and implement some default error-handling behavior. This especially makes sense if you normally expect the method to be present.

Altri suggerimenti

You could define an interface/abstract class with an abstract method on test if the the object is of the interface type, and then call the method you now know exists.

abstract class JsonEncodable {
    Map toJSON();
}

Object _toJson(Object obj) {
    return (obj is JsonEncodable)? obj.toJson() : obj;
}

class Foo implements JsonEncodable {
    Map toJSON() {
        // toJSON implementation
    }
}

With dart:mirrors, you should be able to write something like:

bool respondsTo(dynamic obj, String methodName)
{
    var mirror = reflect(obj);
    return mirror.type.methods.values.map((MethodMirror method) => method.simpleName)
                                     .contains(methodName);
}

It's a work around, but I use a small helper which is basically inline try catch:


String json = ifHasMethodElse(() => obj.toJson(), null)

dynamic ifHasMethodElse(Function closureWithCall, dynamic elseThis) {
  try {
    return closureWithCall();
  } catch (NoSuchMethodError, e) {
    return elseThis;
  }
}

Using a slightly different approach, I'm checking if the given method exists by checking the list of instance functions:

InstanceMirror im = reflect(this);
Symbol fn = new Symbol('$functionName');
if (im.type.instanceMembers.keys.contains(fn)) {
  im.invoke(fn, params);
}

Not lightweight, but doesn't require mirrors (and after rereading the accepted answer, it's basically what the accepted answer suggests as a possibility):

dynamic _toJson(dynamic obj) {
  try {
    return obj.toJson();
  } catch(NoSuchMethodError) {
    return obj;
  }
}

I'd used this for debugging/troubleshooting purposes only - I'd never put this code into production. Ideally, you'd be much better off defining some kind of interface and passing that in as your obj instead of as dynamic, as @thetrompf suggests.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top