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

[.NET] Added .NET 8, NRT and more #253

Closed
wants to merge 5 commits into from
Closed

Conversation

Romfos
Copy link

@Romfos Romfos commented Sep 15, 2024

🤔 What's changed?

  • Update target frameworks: .NET 8, .NET Standard 2.0
  • Run tests for .NET 8 + .NET Framework
  • Adopt C# 12 syntax
  • Added editorconfig
  • Remove non exited files from solution
  • enable warnings as errors
  • Update .NET sdk for github actions

Template generator changes:

  • Enable NRT support
  • Switch from classes to records. Equals, GetHashCode, ToString will be autogenerated
  • Remove copy-paste helpers for every class like Require & RequireNonNull methods
  • Correct implementation for value types

⚡️ What's your motivation?

This PR is result of discussion here cucumber/gherkin#281

🏷️ What kind of change is this?

  • 🏦 Refactoring/debt/DX (improvement to code design, tooling, etc. without changing behaviour)
  • 💥 Breaking change (incompatible changes to the API)

♻️ Anything particular you want feedback on?

📋 Checklist:

  • I agree to respect and uphold the Cucumber Community Code of Conduct
  • I've changed the behaviour of the code
    • I have added/updated tests to cover my changes.
  • My change requires a change to the documentation.
    • I have updated the documentation accordingly.
  • Users should know about my change
    • I have added an entry to the "Unreleased" section of the CHANGELOG, linking to this pull request.

This text was originally generated from a template, then edited by hand. You can modify the template here.

@Romfos
Copy link
Author

Romfos commented Sep 15, 2024

Important:
This PR is a "source breaking change".
Nullable annotations could lead to new warnings in code that depends on this library.
functionally library is the same

.github/workflows/release-nuget.yaml Show resolved Hide resolved
this.<%= capitalize(property_name) %> = <%= property_name %> == null ? null : new <%= type_for(class_name(key), property_name, property) %>(<%= property_name %>);
<%- end -%>
<%- if !isValueType && required -%>
<%= capitalize(property_name) %> = <%= property_name %> ?? throw new ArgumentNullException("<%= capitalize(property_name) %>", "<%= class_name(key) %>.<%= capitalize(property_name) %> cannot be null");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The template was generating "record-like" classes with getters only; in a sense providing an 'immutable' data structure. This PR is adopting the use of records directly, making a stronger statement about immutability.
During the constructor, we previously copyied the contents of enumerable items by calling 'new List(argument) for those properties that were enumerable. This disconnected the Message instance from its source (if one list or the other got changed, the other would not).

This proposed template simply assigns the input argument to the generated property.

@gasparnagy How careful do we need to be about immutability and/or effects of change here?
We could use an ImmutableList here to be explicit about intent and strengthen the behavior, go back to previous behavior of simply copying the list content, or accept this behavior of accepting the List argument as-is.

Copy link
Author

@Romfos Romfos Sep 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ImmutableArray is desirable here, but i will require 1 extra dependency for .NET Standard tfm

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think regardless of what structure we use to store the "immutable" list, we should go back to the previous model of "copying" the items from the input structure, so the simple assignment is not enough.

As this is package is not really used yet, maybe we should be more future proof and go for the ImmutableList accepting the fact that this way we have to add a dependency to System.Collections.Immutable for the .NET Standard 2.0 target (in .NET 8 it is anyway included). So I would say, let's give it a try. If we see problems with this dependendency, we can still roll back.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR is updated

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thx. As far as I see now it takes ImmutableArray as an input, so it will be the responsibility of the caller to ensure the immutability. I think this is good, but I wonder if we should generate an additional convenience overloads for the ctors that just take an IEnumerable<T> and build the ImmutableArray from it.
@clrudolphi Would creating an ImmutableArray for calling the ctors cause any issues for you?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer the constructor signature to accept the IEnumerable as a parameter, and have it create the ImutableArray property.

Copy link
Author

@Romfos Romfos Sep 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree that we need to make a copy inside of constructor without any visible reason.
Maybe you have some code examples why it makes a problem and I can propose a some solution?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's clarify what we mean. Likewise, I do not think it necessary to create copies of the items within the source collections.
The design principle under consideration here is to provide a construction signature that is easy to use. The MS API Design Guidelines suggest that collection parameters be typed as interfaces and of a commonly used type, such as IEnumerable or IReadOnlyCollection.
As a convenience to users of the constructor, we can build an instance of an ImmutableArray from that provided IEnumberable argument and assign that to the property. Doing so doesn't copy the members of the collection (presuming T is a class, not a value type).
Since ImmutableArray is also IEnumerable, within the Messages library we can pass instances of ImmutableArray to constructors of other types (that accept IEnumberable) without needing an overloaded constructor.

If there is a concern about performance or memory overhead of creating a new instance of ImmutableArray out of an input IEnumberable, why not have the constructor test the run-time type of the argument, and if it is already an ImmutableArray, then simply assign it to the property; otherwise new up a new instance?

Copy link
Author

@Romfos Romfos Sep 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, then lets continue to use List copy as we have now

@clrudolphi
Copy link
Contributor

clrudolphi commented Sep 15, 2024 via email

Copy link
Member

@gasparnagy gasparnagy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the overall goal and the concept of the PR is fine.
See my comment on the explicit listing of the .NET 8 target and also we should clean up the "immutable list" problem mentioned by @clrudolphi (see discussion there).

CHANGELOG.md Outdated Show resolved Hide resolved
this.<%= capitalize(property_name) %> = <%= property_name %> == null ? null : new <%= type_for(class_name(key), property_name, property) %>(<%= property_name %>);
<%- end -%>
<%- if !isValueType && required -%>
<%= capitalize(property_name) %> = <%= property_name %> ?? throw new ArgumentNullException("<%= capitalize(property_name) %>", "<%= class_name(key) %>.<%= capitalize(property_name) %> cannot be null");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer the constructor signature to accept the IEnumerable as a parameter, and have it create the ImutableArray property.

@mpkorstanje
Copy link
Contributor

@Romfos did you close this by accident?

@Romfos
Copy link
Author

Romfos commented Sep 22, 2024

@Romfos did you close this by accident?

no, accroding to comment #253 (comment) implementation will stay the same

@mpkorstanje
Copy link
Contributor

Thanks for explaining!

@clrudolphi
Copy link
Contributor

@Romfos Oh, I had missed that this had gotten closed. Even if we leave the collection class type unchanged, I would like to see the other changes proposed within this PR adopted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants