avoid_dynamic_calls 린트 규칙: dynamic 대상에 대한 호출 피하기

avoid_dynamic_calls 린트 규칙: dynamic 대상에 대한 호출 피하기

명시적이든 암시적이든 statically dynamic으로 타입이 지정된 객체에 메서드 호출이나 프로퍼티 접근을 하는 것은 피해야 해요. dynamic 호출은 컴파일 크기와 런타임 성능에 부담이 되고, 정적 분석도 끊어버려 런타임 오류로 이어지기 쉬워요. 이 린트 규칙은 그런 dynamic 호출을 잡아내서 알려줘요.

출처: avoid_dynamic_calls

본문

설명

명시적으로든 암시적으로든 statically dynamic으로 타입이 지정된 객체에 대해 메서드를 호출하거나 프로퍼티에 접근하는 것을 피하세요. dynamic 호출은 모든 런타임 환경과 컴파일러에서 조금씩 다르게 처리되지만, 대부분의 프로덕션 모드(그리고 일부 개발 모드)에서도 dynamic 호출과 관련된 컴파일 크기와 런타임 성능 부담이 있어요.

또한 dynamic으로 타입이 지정된 대상은 대부분의 정적 분석을 끊어버리기 때문에, 제대로 statically typed 된 Dart 코드보다 런타임 NoSuchMethodErrorTypeError로 이어지기 쉬워요.

Object?에 존재하는 메서드와 프로퍼티에는 예외가 있어요.

  • a.hashCode
  • a.runtimeType
  • a.noSuchMethod(someInvocation)
  • a.toString()

...이런 멤버들은 웹 기반 런타임에서 동적으로 디스패치되지만, VM 기반 런타임에서는 그렇지 않아요. 게다가 너무 흔해서 예를 들어 any.toString()이나 any == true를 금지하는 것은 너무 가혹할 거예요.

Function이 하나의 타입임에도 불구하고 의미적으로 dynamic과 거의 동일하다는 사실을 기억하세요. Function으로 타입이 지정된 객체에 대한 호출도 이 린트를 트리거해요.

dynamic 호출은 캐스트 표현식(as dynamic 또는 as Function)에서는 허용돼요.

BAD:

void explicitDynamicType(dynamic object) {
  print(object.foo());
}

void implicitDynamicType(object) {
  print(object.foo());
}

abstract class SomeWrapper {
  T doSomething<T>();
}

void inferredDynamicType(SomeWrapper wrapper) {
  var object = wrapper.doSomething();
  print(object.foo());
}

void callDynamic(dynamic function) {
  function();
}

void functionType(Function function) {
  function();
}

GOOD:

void explicitType(Fooable object) {
  object.foo();
}

void castedType(dynamic object) {
  (object as Fooable).foo();
}

abstract class SomeWrapper {
  T doSomething<T>();
}

void inferredType(SomeWrapper wrapper) {
  var object = wrapper.doSomething<Fooable>();
  object.foo();
}

void functionTypeWithParameters(Function() function) {
  function();
}

활성화 방법

avoid_dynamic_calls 규칙을 활성화하려면 analysis_options.yaml 파일의 linter > rules 아래에 규칙을 추가하면 돼요.

linter:
  rules:
    - avoid_dynamic_calls

대신 YAML 맵 문법을 사용해서 린트 규칙을 설정하고 있다면, linter > rules 아래에 avoid_dynamic_calls: true를 추가하면 돼요.

linter:
  rules:
    avoid_dynamic_calls: true

더 알아보기