Как вы можете создавать динамические геттеры / сеттеры в dart

#dart #dart-mirrors

#dart #dart-зеркала

Вопрос:

Я пытаюсь воссоздать функциональность djangos QueryDict и создать объект, которому можно присвоить карту, и он будет частной переменной в объекте, а геттеры / сеттеры используются для динамического извлечения из карты. Мне удалось воссоздать метод get (), но я теряюсь при динамическом получении значения. Вот что у меня есть до сих пор:

 class QueryMap {
  Map _data;

  QueryMap(Map this._data);

  dynamic get(String key, [var defaultValue]) {
    if(this._data.containsKey(key)) {
      return this._data[key];
    } else if(defaultValue) {
      return defaultValue;
    } else {
      return null;
    }
  }
}
  

Вот страница djangos о том, как это работает:
https://docs.djangoproject.com/en/dev/ref/request-response/#django.http .QueryDict.getitem

Ответ №1:

Вы можете переопределить noSuchMethod (эмулирующие функции)

 @proxy
class QueryMap {
  Map _data = new Map();

  QueryMap();

  noSuchMethod(Invocation invocation) {
    if (invocation.isGetter) {
      var ret = _data[invocation.memberName.toString()];
      if (ret != null) {
        return ret;
      } else {
        super.noSuchMethod(invocation);
      }
    }
    if (invocation.isSetter) {
      _data[invocation.memberName.toString().replaceAll('=', '')] =
          invocation.positionalArguments.first;
    } else {
      super.noSuchMethod(invocation);
    }
  }
}
void main() {
  QueryMap qaueryMap = new QueryMap();
  qaueryMap.greet = "Hello Dart!";
  print(qaueryMap.greet); //Hello Dart!
}
  

Как отметил @PixelElephant с внешней картой, вы должны использовать реальные имена методов в качестве ключей карты:

 import 'dart:mirrors';
@proxy
class QueryMap {
  Map _data;

  QueryMap(this._data);

  noSuchMethod(Invocation invocation) {
    if (invocation.isGetter) {
      var ret = _data[MirrorSystem.getName(invocation.memberName)];
      if (ret != null) {
        return ret;
      } else {
        super.noSuchMethod(invocation);
      }
    }
    if (invocation.isSetter) {
      _data[MirrorSystem.getName(invocation.memberName).replaceAll('=', '')] =
          invocation.positionalArguments.first;
    } else {
      super.noSuchMethod(invocation);
    }
  }
}
void main() {
  Map myMap = new Map();
  myMap["color"] = "red";
  QueryMap qaueryMap = new QueryMap(myMap);
  qaueryMap.greet = "Hello Dart!";
  print(qaueryMap.greet); //Hello Dart!
  print(qaueryMap.color); //red
}
  

Чтобы избежать использования зеркал, вы можете использовать сопоставление с образцом при сериализации строки символа или преобразовании внешних ключей карты:

 @proxy
class QueryMap {
  Map _data;

  QueryMap(Map data) {
    _data = new Map();
    data.forEach((k, v) => _data[new Symbol(k).toString()] = v);
  }

  noSuchMethod(Invocation invocation) {
    if (invocation.isGetter) {
      var ret = _data[invocation.memberName.toString()];
      if (ret != null) {
        return ret;
      } else {
        super.noSuchMethod(invocation);
      }
    }
    if (invocation.isSetter) {
      _data[invocation.memberName.toString().replaceAll('=', '')] =
          invocation.positionalArguments.first;
    } else {
      super.noSuchMethod(invocation);
    }
  }
}
void main() {
  Map myMap = new Map();
  myMap["color"] = "red";
  QueryMap qaueryMap = new QueryMap(myMap);
  qaueryMap.greet = "Hello Dart!";
  print(qaueryMap.greet); //Hello Dart!
  print(qaueryMap.color); //red
}
  

Комментарии:

1. Обратите внимание, что memberName это a Symbol , и вы должны вызвать MirrorSystem.getName его, чтобы получить строку, которую вы можете использовать в качестве ключа (вместо toString ).

2. @PixelElephant Я старался избегать использования зеркал в любой форме или форме, потому что они могут повлиять на производительность.

3. Да, но они нужны здесь. Ваш код работает только потому, что вы оба устанавливаете и получаете, используя noSuchMethod. Если бы вы использовали уже созданную карту, например Map map = {'greet': 'Hello Dart'}; , код потерпел бы неудачу, поскольку он пытается получить Symbol('greet') , а не просто greet .

4. @PixelElephant Спасибо, это верная точка зрения. Я забыл, что он использует внешнюю карту.

5. Это довольно гениально! Итак, я предполагаю, что последний метод будет работать и на flutter, где зеркала не разрешены? Также кто-нибудь имеет представление о снижении производительности при использовании этого? Это действительно помогает сократить много хлопот в нашей кодовой базе, поэтому у меня возникает соблазн использовать его