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?

有帮助吗?

解决方案

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.

其他提示

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.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top