【问题标题】:How can I get an extension method to change the original object in Dart?如何获得扩展方法来更改 Dart 中的原始对象?
【发布时间】:2021-03-09 11:55:11
【问题描述】:

我有bounds。应使用新的 LatLng 扩展边界。

var bounds = LatLngBounds();
for (var latlng in _list) bounds.extend(latlng);

我想实现这个扩展:

extension LatLngBoundsExtend on LatLngBounds {
  extend(LatLng _latLng){
    //I want to change the object which this method is called from
    this = LatLngBounds(southwest: /* some magic code*/, northeast: /* some magic code*/   );

  }
}

【问题讨论】:

  • 您不能更改带有扩展名的原始对象。您最好的选择是返回一个包含已编辑数据的新对象,但您会失去使用扩展程序的兴趣。
  • Dart 扩展应该有一些改进!

标签: flutter class object dart extension-methods


【解决方案1】:

如果原始对象的属性不是最终的,则可以更改原始对象。 This is not the case for LatLngBounds.southwest and LatLngBounds.northeast.

【讨论】:

  • 我的意思是改变对象。但不是属性。
【解决方案2】:

如果我没记错的话,LatLngBounds 是一个不可变类,表示经纬度对齐的矩形。

final myLatLngBounds = LatLngBounds(southwest, northeast);
myLatLngBounds.extend(latlng);

如果这个extend 修改了this,你将如何访问它? ?

我认为这个extend 应该是某种状态管理的一部分。

查看以下内容,使用Riverpod Hooks package:

您无需更改对象(因为它是不可变的),而是在扩展中返回一个新对象:

extension RectX on Rect {
  Rect extend(Offset offset) {
    return Rect.fromLTRB(
      min(left, offset.dx),
      min(top, offset.dy),
      max(right, offset.dx),
      max(bottom, offset.dy),
    );
  }
}

然后,当您需要修改对象时,您可以在 Flutter 或任何其他 State Management System 管理的 State Object 上工作:

final rectProvider =
    StateNotifierProvider<RectNotifier>((ref) => RectNotifier());

class RectNotifier extends StateNotifier<Rect> {
  RectNotifier([Rect state])
      : super(state ?? Rect.fromLTRB(100, 100, 200, 200));

  void extend(Offset offset) {
    state = state.extend(offset);
  }
}

这是一个最小的工作示例:

import 'dart:math' show min, max;

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

void main() {
  runApp(
    ProviderScope(
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        title: 'Flutter Demo',
        home: HomePage(),
      ),
    ),
  );
}

class HomePage extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final rect = useProvider(rectProvider.state);
    return Scaffold(
      body: GestureDetector(
        onTapDown: (details) =>
            context.read(rectProvider).extend(details.localPosition),
        child: Container(
          color: Colors.black12,
          padding: EdgeInsets.all(16.0),
          child: Stack(
            children: [
              Positioned(
                top: rect.top,
                left: rect.left,
                child: Container(
                    width: rect.width,
                    height: rect.height,
                    color: Colors.amber.shade400),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

final rectProvider =
    StateNotifierProvider<RectNotifier>((ref) => RectNotifier());

class RectNotifier extends StateNotifier<Rect> {
  RectNotifier([Rect state])
      : super(state ?? Rect.fromLTRB(100, 100, 200, 200));

  void extend(Offset offset) {
    state = state.extend(offset);
  }
}

extension RectX on Rect {
  Rect extend(Offset offset) {
    return Rect.fromLTRB(
      min(left, offset.dx),
      min(top, offset.dy),
      max(right, offset.dx),
      max(bottom, offset.dy),
    );
  }
}

【讨论】:

    【解决方案3】:

    如果字段不是最终字段,您可以更改它们。但是您不能将对象更改为this = new Class()。

    PS。 southwest 和 northeast 是 LatLngBounds 的 final 字段。所以你不能用扩展来改变它们 https://github.com/flutter/plugins/blob/master/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/location.dart

    【讨论】:

      【解决方案4】:

      扩展方法可以变异 this,但它们不能重新分配/重新绑定它。请注意,这也适用于普通方法,并不特定于扩展方法。

      假设重新分配this 是可能的。考虑以下代码:

      var foo = Foo();
      var sameFoo = foo;
      
      foo.reassignThis();
      

      foo 和 sameFoo 是否仍然指代同一个对象?如果他们现在提到不同的对象,那将是令人惊讶的。但是,如果它们仍然引用同一个对象,则意味着 VM/运行时需要能够轻松快速地找到对对象的所有引用,以便更新它们。

      即使虚拟机/运行时可以做到这一点,请考虑:

      class Base {
        Base(this.x);
      
        int x;
      
        void reassignThis() {
          this = Base(x + 1); // Not legal!
        }
      }
      
      class Derived extends Base {
        Derived(int x) : super(x);
      
        int y;
      }
      
      void main() {
        var derived = Derived(0);
        derived.reassignThis();
        print(derived.y);
      }
      

      调用reassignThis() 之后,derived 会是什么?它仍然是Derived 对象吗?它只是一个Base 对象吗?

      重新分配this 是不可能的,也不是可以改进的;基本上没有多大意义。


      现在,您可以实现以下内容,而不是重新分配 this:

      class MyLatLngBounds implements LatLngBounds {
        MyLatLngBounds(this._actual);
      
        LatLngBounds _actual;
      
        LatLng get northeast => _actual.northeast;
        LatLng get southwest => _actual.southwest;
        // ... other methods that forward to [_actual] ...
      
        void extend(LatLng _latLng) {
          _actual = LatLngBounds(/* some magic code */);
        }
      }
      

      然后尝试在任何地方使用MyLatLngBounds 而不是LatLngBounds。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-05-03
        • 2013-01-28
        • 2015-03-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-23
        • 2022-07-08
        相关资源
        最近更新 更多