Add an abstract class Disposable that contains a disposable method. To standardize work with classes that need to be released.
Dart currently uses either close (for sinks) or cancel (for sources) to make the end of operation.
We can probably add a Disposable/Closable/Cancelable interface with one of those methods, and make the corresponding classes implement that interface, but it's unlikely that we can add methods to the existing interfaces.
The idea is in the standard interface that would serve as a marker that the object that implements it needs to be released.
Just like in C# https://docs.microsoft.com/en-us/dotnet/api/system.idisposable?view=netcore-3.1
This will provide a convenient check for other code that will work with these classes.
if (obj is Disposable) {
obj.dispose();
}
Instead of this:
try {
(obj as dynamic).dispose();
} on Object catch (_) {}
For dispose to actually work, it would have to return a Future because some disposable objects need asynchronous clean-up when you dispose them.
That means that the interface must have dispose return a Future, which again means that all operations which work with disposable objects must be asynchronous to handle that future.
That suggests that Dart object disposal is not homogeneous enough to be supported by a single interface. Having two interfaces (Disposable, AsyncDisposable) seems like it would just make things more complicated for everybody.
Dart has first class functions, so I'd prefer composition to inheritance. Instead of making objects Disposable, introduce a Disposer interface and let everybody provide their own way to be disposable.
That sound like it would be something like:
abstract class Disposer<T, R> {
Disposer(T value, R dispose()) = _Disposer<T, R>;
T get value;
R dispose();
}
class _Disposer<T> implements Disposable<T> {
final T value;
final R Function() _dispose;
_Disposable(this.value, this._dispose);
R dispose() => _dispose();
}
Then, if you want to dispose something, you just wrap it in a Disposer. You can declare extensions for common classes:
extension SubscriptionDisposer<T> on StreamSubcription<T> {
Disposer<StreamSubscription<T>, Future<void>> get disposer => Disposer(this, this.cancel);
}
We can use generic Disposable.
void main() {
final objA = D(A());
final objB = D(B());
final objC = D(C());
objA.dispose();
objB.dispose();
objC.dispose();
}
abstract class Disposable<R> {
R dispose();
}
class A implements Disposable<Future<int>> {
@override
Future<int> dispose() async {
print('A disposed');
return Future(() => 1);
}
}
class B implements Disposable<void> {
@override
void dispose() {
print('B disposed');
}
}
class C {
void dispose() {
print('----');
}
}
class D {
final Object obj;
D(this.obj);
void dispose() {
if (obj is Disposable) {
(obj as Disposable).dispose();
}
}
}
A generic disposable means that generic disposal handling code cannot assume that the return value is a Future, nor that it isn't a Future. All dispose abstractions need to code for both, and at that point, we might as well define it as FutureOr<void> dispose().
(Which is generally discouraged as API design because it puts a burden on the receiver).
I'm understood, thank you.
Most helpful comment
The idea is in the standard interface that would serve as a marker that the object that implements it needs to be released.
Just like in C# https://docs.microsoft.com/en-us/dotnet/api/system.idisposable?view=netcore-3.1
This will provide a convenient check for other code that will work with these classes.
Instead of this: