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

Extension methods outside builder for optional properties #28

Open
Sam13 opened this issue Jul 8, 2024 · 4 comments
Open

Extension methods outside builder for optional properties #28

Sam13 opened this issue Jul 8, 2024 · 4 comments
Labels
theory crafting Let's discuss ideas

Comments

@Sam13
Copy link

Sam13 commented Jul 8, 2024

What about generating additional extension methods for optional properties which can be populated in any order after fluent builder was fully executed?

Example:

[FluentApi]
public class Student
{
    [FluentMember(0)]
    public string FirstName { get; private set; }

    [FluentMember(1)]
    public string LastName { get; private set; }

    ...
    public string? FavoriteFood { get; private set; }
    public string? Hobby { get; private set; }
 }

Usage:

Student alice1 = CreateStudent
  .WithFirstName("Alice").
  .WithLastName("TestName")
  .WithHobby("Reading")
  .WithFavoriteFood("Pizza");

Student alice2 = CreateStudent
  .WithFirstName("Alice").
  .WithLastName("TestName")
  .WithFavoriteFood("Pizza")
  .WithHobby("Reading");

Generated code (pseudo code):

public static Student WithHobby(this Student student, string hobby)
{
  student.Hobby = hobby; // Probably set via reflection
  return student;
}
@m31coding
Copy link
Owner

m31coding commented Jul 16, 2024

Hi @Sam13,

Thank you for this!

What you want to achieve is already possible like so:

[FluentApi]
public class Student
{
    [FluentMember(0)]
    public string FirstName { get; private set; }

    [FluentMember(1)]
    public string LastName { get; private set; }

    [FluentMember(2)]
    [FluentContinueWith(2)]
    public string? FavoriteFood { get; private set; }

    [FluentMember(2)]
    [FluentContinueWith(2)]
    public string? Hobby { get; private set; }

    [FluentMethod(2)]
    public void Create()
    {
    }
}

A final method, called Create in this example, is needed in the last step to conclude the setting of the optional properties:

Student alice1 = CreateStudent
  .WithFirstName("Alice")
  .WithLastName("TestName")
  .WithHobby("Reading")
  .WithFavoriteFood("Pizza")
  .Create();

Admittedly, the attributes that have to be specified for this group of optional properties are not very intuitive. Maybe we can come up with a better syntax for this.

Happy coding!
Kevin

@Sam13
Copy link
Author

Sam13 commented Jul 18, 2024

Hi @m31coding

Those methods are still part of the builder, aren't they?
What I would prefer are ordinary extension methods on the original type.
This does not need a dummy final method.

As example the method of my first post

public static Student WithHobby(this Student student, string hobby)
{
  student.Hobby = hobby; // Probably set via reflection
  return student;
}

What do you think?

@m31coding
Copy link
Owner

Ah, now I understand your approach. As you pointed out, no dummy final method would be needed. However, the downside is that the extension method remains available after the instance is constructed, which undermines the immutability of the Student class.

I lean towards bundling the methods for building the instance within the builder class. I agree that the final dummy method is not ideal. Another way to avoid it could be using an implicit conversion operator. For this to work, the fluent API would need to expose a class instance (rather than an interface) in the final step, allowing for the setting of optional properties and the implicit conversion to a Student instance.

@Sam13
Copy link
Author

Sam13 commented Jul 19, 2024

Ah, now I understand your approach. As you pointed out, no dummy final method would be needed. However, the downside is that the extension method remains available after the instance is constructed, which undermines the immutability of the Student class.

That's exactly what I want - the extension methods should be available after construction.
This allows creating an instance which can be customized later on - even in code outside of my control...
With the builder you create the instance with the mandatory properties and with the extension you can customize the optional properties.

If you want immutable data structures you may use C# records?

Assuming Student is a record the extension methods would look like that:

public static Student WithHobby(this Student student, string hobby)
{
  return student with { Hobby = hobby };
}

@m31coding m31coding added the theory crafting Let's discuss ideas label Sep 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
theory crafting Let's discuss ideas
Projects
None yet
Development

No branches or pull requests

2 participants