Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Partial Classes and Methods feature request #252

Open
listepo opened this issue Mar 3, 2019 · 16 comments
Open

Partial Classes and Methods feature request #252

listepo opened this issue Mar 3, 2019 · 16 comments
Labels
augmentation-libraries request Requests to resolve a particular developer problem

Comments

@listepo
Copy link

listepo commented Mar 3, 2019

Would like to have something similar in a dart. Especially when generating code.
Example: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods

@roman-vanesyan
Copy link

Possibly, covered by #177

@eernstg
Copy link
Member

eernstg commented Mar 4, 2019

Right, scoped class extension (#177) is intended to allow third-party developers (that is, for a class C, developers who won't edit the code of C because they can't or because it's somebody else's code) to add proper instance methods to an existing type hierarchy (a typical example would be a tree of classes that extend each other), with the ability to inherit and/or override method implementations, and the ability at call sites to have all the usual typing properties (e.g., that a method may accept more optional arguments than the methods that it overrides).

For code generation you might be able to decide up front that you can always edit the code of the target class(es), in which case you might be able to use a mechanism based on part files.

Such a mechanism would allow you to patch a class C in the main file by adding method implementations and (possibly private) declarations from the part file in a "partial" declaration of C, and it could basically be considered to be similar to an include file mechanism (the effective source code in the main file is obtained by adding stuff from the part files).

This is simpler than scoped class extension, but it will only work in cases where you have enough control over the target libraries to, at least, be able to introduce those part declarations into the target libraries and generate the part files.

Also, you can control scoped class extension because it is scoped. So if two third-party organizations wish to add a method named foo to the same class(es) then a part based approach will just incur some "duplicate declaration" errors at compile-time. But with scoped class extensions you'd make the choice in each library that contains a call site that you import just one of those conflicting extensions, which eliminates the conflict.

@a14n
Copy link

a14n commented Mar 4, 2019

A common pattern to generate code (with source_gen):

// file.dart
part 'file.gen.dart';
class A extends $A {
}

// file.gen.dart
part of 'file.dart';
class $A {
  myAwesoneGeneration() => null;
}

With this pattern it's not possible/simple to generate constructors or static member.

@eernstg
Copy link
Member

eernstg commented Mar 4, 2019

Right, and it's also not possible to add a method to num, int, and double.

@lrhn
Copy link
Member

lrhn commented Mar 11, 2019

C# partial classes do not allow you to add members to existing non-partial classes.

You can only have multiple parts of a class declared to be partial, and still only in the same assembly (compilation unit). In Dart, I'd say it would have to be the same library.

@rrousselGit
Copy link

rrousselGit commented Jul 10, 2019

With the weird behavior of extensions methods tear-off that are never equal to themselves #425, I think partial classes are important.

Without partial classes, code-generators will instead generate extension members.
But this could be very "dangerous" because of the tear-off issue.

@Levi-Lesches
Copy link

This would be so helpful.

I'm following up with code-generation (#1482) and the question of where to put the code is starting to be an issue for me. I've experimented with extension, mixins, extends, external, and part, and I can't figure out a good convention that works in all cases. To demonstrate why, consider the simple and commonly-requested example of an AutoDispose code-gen, which automatically calls dispose on fields marked with shouldDispose in the following class:

// my_widget.dart
part "my_widget.g.dart";

@AutoDispose()
class MyState extends State<MyWidget> {
  /// This annotation is defined by my macro. It tells it to include this field in [dispose].
  @shouldDispose
  final TextEditingController controller;

  @override
  Widget build(BuildContext context) => Scaffold(
    // ...
  );
}

I tried extensions, which doesn't work because you can't override members with extensions.

// my_widget.g.dart
part of "my_widget.dart";

extension on MyState {
  @override  // INFO: The method doesn't override an inherited method.
  void dispose() {  // INFO: The declaration 'dispose' isn't referenced
    controller.dispose();
    super.dispose();
  }
}

I tried mixins, but because I want to declare the mixin on MyState, it gets ugly:

// my_widget.dart
part "my_widget.g.dart";

// The following is nice, but does not work: 
class MyState with GeneratedMixin { } 
// Error: 'MyState' can't be a superinterface of itself: MyState, GeneratedMixin, MyState.
mixin GeneratedMixin on MyState { } 
// Error: 'GeneratedMixin' can't be a superinterface of itself: GeneratedMixin, MyState, GeneratedMixin.

// So I had to resort to this
class TempState extends State<MyWidget> { /* the body of MyState */ }
class MyState = TempState with GeneratedMixin;
// my_widget.g.dart
part of "my_widget.dart";

mixin GeneratedMixin on TempState { 
  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }
}

I initially played with extensions, but partial classes fit better here. That's because extensions have three key limitations:

  1. Extensions cannot define constructors
  2. Extensions cannot override members. Particularly not members of Object.
  3. Extensions cannot define fields

Partial classes fix all three of these, and allow generated code to live happily side-by-side with human code, while allowing complete separation. Broadly, both part/part of and extension have a common goal: to split code across declarations/files. Partial classes will fill in the gaps.

PS: I'm in favor of partial class X instead of class X partial. It fits better with mixin, abstract, extension, and var which describe the declaration itself instead of implements, extends, and on, which only slightly modify the declaration (and thus go after).

@Levi-Lesches
Copy link

My proposal for code generation is at #1565. Please take a look, I believe it fully utilizes the potential of partial classes. It's also dependent on this issue, so add my vote to the count!

@Vardiak
Copy link

Vardiak commented Apr 22, 2021

This would be really useful, is it on Dart team's roadmap ?

@munificent
Copy link
Member

Not on the roadmap currently, no.

@ciriousjoker
Copy link

Any updates on this?

@lrhn
Copy link
Member

lrhn commented Apr 30, 2022

Check out the "augmentation" feature.

@semog
Copy link

semog commented Nov 15, 2023

Partial classes are definitely the way to go. Seems like a natural evolution of the language. Extensions are great for what they are, but we have some very large classes that now must fit within a single source file so that private variables can be accessed. This is making code maintenance more difficult than it needs to be. And before someone says we should refactor our code, this is server side code and a single class encapsulates a single topic of concern. That topic can have many functions to it that make the topic cohesive and complete.

Please add this feature to the near-term road map. Thanks!

@DYT-Flyer
Copy link

Was this implemented?

@mateusfccp
Copy link
Contributor

mateusfccp commented May 16, 2024

Was this implemented?

No.

Maybe this is partially or fully solved by class augmentations, but they were not released yet.

@munificent
Copy link
Member

@mateusfccp is correct. We haven't shipped anything yet, but are actively working on augmentations which is cover this and many more use cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
augmentation-libraries request Requests to resolve a particular developer problem
Projects
Development

No branches or pull requests