Dart:具有可为空属性的自定义“copyWith”方法

Gus*_*ger 32 dart dart-null-safety

我正在尝试为我的类创建一个“copyWith”方法,它适用于大多数场景。

问题是当我尝试将可为 null 的属性设置为 null 时,因为我的函数无法识别它是否是有意的。

前任。:

class Person {
  final String? name;
  
  Person(this.name);
  
  Person copyWith({String? name}) => Person(name ?? this.name);
}

void main() {
  final person = Person("Gustavo");
  print(person.name); // prints Gustavo
  
  // I want the name property to be nul
  final person2 = person.copyWith(name: null);
  print(person2.name); // Prints Gustavo
}
Run Code Online (Sandbox Code Playgroud)

有谁知道这种情况的一些解决方法?这真的很困扰我,我不知道如何避免这种情况。

Dan*_*ndt 45

一种解决方案是使用函数来设置该值。这给你更多的选择。

  • 未提供的功能:null
    • 这不会改变该值
  • 提供并返回 null 的函数:() => null
    • 这会将值设置为null
  • 返回名称的函数:() => 'Gustavo'
    • 这会将值设置为Gustavo
class Person {
  final String? name;

  Person(this.name);

  Person copyWith({String? Function()? name}) =>
      Person(name != null ? name() : this.name);
}

void main() {
  final person = Person('Gustavo');
  print(person.name); // prints Gustavo

  // I want the name property to be nul
  final person2 = person.copyWith(name: () => null);
  print(person2.name); // Prints null

  final person3 = person.copyWith(name: () => 'new name');
  print(person3.name); // Prints new name

  final person4 = person.copyWith();
  print(person4.name); // Prints Gustavo
}
Run Code Online (Sandbox Code Playgroud)

它使设置名称稍微麻烦一些,但好的一面是,如果您尝试直接传递字符串,编译器会告诉您提供了错误的类型,因此会提醒您向其中添加() =>

  • 感谢您的贡献。在我看来,这种方法更容易理解,并且不需要任何额外的类/包。好一个 :) (2认同)
  • 同意这也是最简单、最清晰的解决方案,但是我只是将 `copyWith` 方法重命名为 `nullableCopyWith` 之类的名称,以便让使用您的方法的每个人都清楚地知道,它不会是标准的 `copyWith` 方法预计。 (2认同)

iDe*_*ode 23

受到@jamesdlin回答的启发:

您所需要做的就是提供一个包装器。考虑这个例子:

class Person {
  final String? name;

  Person(this.name);

  Person copyWith({Wrapped<String?>? name}) =>
      Person(name != null ? name.value : this.name);
}

// This is all you need:
class Wrapped<T> {
  final T value;
  const Wrapped.value(this.value);
}

void main() {
  final person = Person('John');
  print(person.name); // Prints John

  final person2 = person.copyWith();
  print(person2.name); // Prints John

  final person3 = person.copyWith(name: Wrapped.value('Cena'));
  print(person3.name); // Prints Cena

  final person4 = person.copyWith(name: Wrapped.value(null));
  print(person4.name); // Prints null
}
Run Code Online (Sandbox Code Playgroud)


小智 17

有多种选择:

1.价值获取者

class B {
  const B();
}

class A {
  const A({
    this.nonNullable = const B(),
    this.nullable,
  });

  final B nonNullable;
  final B? nullable;

  A copyWith({
    B? nonNullable,
    ValueGetter<B?>? nullable,
  }) {
    return A(
      nonNullable: nonNullable ?? this.nonNullable,
      nullable: nullable != null ? nullable() : this.nullable,
    );
  }
}

const A().copyWith(nullable: () => null);
const A().copyWith(nullable: () => const B());
Run Code Online (Sandbox Code Playgroud)

2. Quiver 套件中可选

class B {
  const B();
}

class A {
  const A({
    this.nonNullable = const B(),
    this.nullable,
  });

  final B nonNullable;
  final B? nullable;

  A copyWith({
    B? nonNullable,
    Optional<B>? nullable,
  }) {
    return A(
      nonNullable: nonNullable ?? this.nonNullable,
      nullable: nullable != null ? nullable.value : this.nullable,
    );
  }
}

const A().copyWith(nullable: const Optional.fromNullable(null));
const A().copyWith(nullable: const Optional.fromNullable(B()));
Run Code Online (Sandbox Code Playgroud)

3.copyWith作为字段

class _Undefined {}

class B {
  const B();
}

class A {
  A({
    this.nonNullable = const B(),
    this.nullable,
  });

  final B nonNullable;
  final B? nullable;

  // const constructor no more avaible
  late A Function({
    B? nonNullable,
    B? nullable,
  }) copyWith = _copyWith;

  A _copyWith({
    B? nonNullable,
    Object? nullable = _Undefined,
  }) {
    return A(
      nonNullable: nonNullable ?? this.nonNullable,
      nullable: nullable == _Undefined ? this.nullable : nullable as B?,
    );
  }
}

A().copyWith(nullable: null);
A().copyWith(nullable: const B());
Run Code Online (Sandbox Code Playgroud)

4.copyWith重定向构造函数

class _Undefined {}

class B {
  const B();
}

abstract class A {
  const factory A({
    B nonNullable,
    B? nullable,
  }) = _A;

  const A._({
    required this.nonNullable,
    this.nullable,
  });

  final B nonNullable;
  final B? nullable;

  A copyWith({B? nonNullable, B? nullable});
}

class _A extends A {
  const _A({
    B nonNullable = const B(),
    B? nullable,
  }) : super._(nonNullable: nonNullable, nullable: nullable);

  @override
  A copyWith({B? nonNullable, Object? nullable = _Undefined}) {
    return _A(
      nonNullable: nonNullable ?? this.nonNullable,
      nullable: nullable == _Undefined ? this.nullable : nullable as B?,
    );
  }
}

const A().copyWith(nullable: null);
const A().copyWith(nullable: const B());
Run Code Online (Sandbox Code Playgroud)

5. copyWith重定向构造函数2

class _Undefined {}

class B {
  const B();
}

abstract class A {
  const factory A({
    B nonNullable,
    B? nullable,
  }) = _A;

  const A._();

  B get nonNullable;
  B? get nullable;

  A copyWith({B? nonNullable, B? nullable});
}

class _A extends A {
  const _A({
    this.nonNullable = const B(),
    this.nullable,
  }) : super._();

  @override
  final B nonNullable;
  @override
  final B? nullable;

  @override
  A copyWith({B? nonNullable, Object? nullable = _Undefined}) {
    return _A(
      nonNullable: nonNullable ?? this.nonNullable,
      nullable: nullable == _Undefined ? this.nullable : nullable as B?,
    );
  }
}

const A().copyWith(nullable: null);
const A().copyWith(nullable: const B());
Run Code Online (Sandbox Code Playgroud)


jam*_*lin 8

Person.name被声明为不可空,因此不可能为其copyWith分配空值。如果您想要Person.name可为空,您应该问自己是否真的想要区分null和 空字符串。通常你不会。

如果您确实希望同时允许null 空字符串,那么您将需要使用其他一些哨兵值:

class Person {
  static const _invalid_name = '_invalid_name_';

  final String? name;
  
  Person(this.name);
  
  Person copyWith({String? name = _invalid_name}) =>
    Person(name != _invalid_name ? name : this.name);
}
Run Code Online (Sandbox Code Playgroud)

或者您需要将其包装在另一个类中,例如:

class Optional<T> {
  final bool isValid;
  final T? _value;

  // Cast away nullability if T is non-nullable.
  T get value => _value as T;

  const Optional()
      : isValid = false,
        _value = null;
  const Optional.value(this._value) : isValid = true;
}

class Person {
  final String? name;

  Person(this.name);

  Person copyWith({Optional<String?> name = const Optional()}) =>
      Person(name.isValid ? name.value : this.name);
}

void main() {
  final person = Person("Gustavo");
  print(person.name);

  final person2 = person.copyWith(name: Optional.value(null));
  print(person2.name);
}
Run Code Online (Sandbox Code Playgroud)

有一些现有的包实现了Optional类似的类,可能可以帮助您。