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

[System.Net] Introduce CIDR IPNetwork #82779

Merged
merged 14 commits into from
Mar 27, 2023

Conversation

mddddb
Copy link
Contributor

@mddddb mddddb commented Feb 28, 2023

PR for a type created based on #79946

@ghost
Copy link

ghost commented Feb 28, 2023

Tagging subscribers to this area: @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

Issue Details

null

Author: mddddb
Assignees: mddddb
Labels:

area-System.Net

Milestone: -

Copy link
Member

@antonfirsov antonfirsov left a comment

Choose a reason for hiding this comment

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

Some comments regarding validation, more to come.

You also need to generate reference source: https://github.com/dotnet/runtime/blob/main/docs/coding-guidelines/updating-ref-source.md#for-most-assemblies-within-libraries

(I can do it for you if you want.)

@mddddb
Copy link
Contributor Author

mddddb commented Mar 1, 2023

Some comments regarding validation, more to come.

You also need to generate reference source: https://github.com/dotnet/runtime/blob/main/docs/coding-guidelines/updating-ref-source.md#for-most-assemblies-within-libraries

(I can do it for you if you want.)

@antonfirsov I was actually having a problem with this one, even tests were not able to see IPNetwork😅 Thanks!
Will give it a read and make the changes later, or if you can make the change - I would appreciate that

@antonfirsov
Copy link
Member

/azp run runtime-extra-platforms

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@antonfirsov antonfirsov marked this pull request as ready for review March 20, 2023 17:18
Copy link
Member

@antonfirsov antonfirsov left a comment

Choose a reason for hiding this comment

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

@dotnet/ncl this is ready for review now. Since I changed most of the original implementation, it would need an approval from someone else.

Downlevel packaging will be implemented in a separate PR.

/// <returns>The <see cref="string"/> containing the <see cref="IPNetwork"/>'s CIDR notation.</returns>
public override string ToString()
{
return $"{BaseAddress}/{PrefixLength}";
Copy link
Member

Choose a reason for hiding this comment

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

Is what this renders for default(IPNetwork).ToString() desirable? Seems like here and below we should special-case BaseAddress == null to be the same as BaseAddress == IPAddress.Any.

Copy link
Member

Choose a reason for hiding this comment

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

Not big fan of making default(IPNetwork) equivalent to another arbitrary state. Encountering such instances is a sign of a programming error in most of the cases. Would it be very unconventional if ToString() returned something like <uninitialized> instead?

Copy link
Member

Choose a reason for hiding this comment

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

IPAddress.Any

Note that it could be also IPAddress.IPv6Any.

Copy link
Member

Choose a reason for hiding this comment

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

Not big fan of making default(IPNetwork) equivalent to another arbitrary state.

I think we need to make default(IPNetwork) valid. If you were forced to add a new IPNetwork() constructor, what would its behavior be? What would an IPNetwork.Empty do? Etc. Whatever that is, I think we should make default(IPNetwork) behave equivalently. Having the default value of a struct randomly fail is an anti-pattern. We typically only do it on our structs not meant to be used directly, e.g. helper types used by a compiler or other tools.

Copy link
Member

@antonfirsov antonfirsov Mar 21, 2023

Choose a reason for hiding this comment

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

If you were forced to add a new IPNetwork() constructor, what would its behavior be? What would an IPNetwork.Empty do?

One can argue that default(IPNetwork) is an empty network that doesn't contain any IPAddress, however note that it's not possible to define such an instance with an IP/prefix-length pair.

Having the default value of a struct randomly fail is an anti-pattern.

I have got rid of the exceptions, so the current PR behavior of default(IPNetwork) is what I just described above ("empty network"). The methods don't fail anymore, we just don't pretend that there is a CIDR pair when none was defined.

Personally I find this to be a good compromise, but if you think it is not acceptable, and we should assign an IPAddress to the BaseAddress == null case, I don't think it would be a consistent behavior to special-case it only only in method implementations, we should rather do something like:

public readonly struct IPNetwork
{
    private readonly IPAddress _address;

    public IPAddress BaseAddress => _address ?? IPAddress.Any;
}

but I personally find this worse because IPAddress.Any is an arbitrary choice.

Copy link
Member

@stephentoub stephentoub Mar 22, 2023

Choose a reason for hiding this comment

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

default(IPNetwork) is all zeros. Imagine IPAddress were a struct rather than a class. What would its all zero's value be? That's IPAddress.Any. Hence why I was suggesting it behave like IPAddress.Any + PrefixLength==0.

I do think you should make BaseAddress return something if the address is null. Otherwise, the nullable annotations are wrong: it's advertising it will always return something non-nullable, but it might return null. Alternatively, you could make the property be public IPAddress? BaseAddress { get; }.

And then Contains would also need to be tweaked, either making it return true rather than false if the address is null, or if we make it so that BaseAddress is never null (my preference), just using whatever it is and computing the answer based on that without any special-casing.

Copy link
Member

Choose a reason for hiding this comment

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

That's IPAddress.Any

Or IPAddress.IPv6Any, depends on the implementation. I don't think this case is similar to eg. default(Memory<T>). Using default(IPNetwork) is a programming bug in 99% of the cases. As a user, I would expect a sw library to help detecting it and "fail fast" instead of hiding it with some arbitrary "correct" behavior. But based on your feedback, I assume this is not our preference with BCL structs. I wish we had design guidelines covering such details.

Copy link
Member

Choose a reason for hiding this comment

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

I wish we had design guidelines covering such details.

We do. They say such values should work.
cc: @bartonjs

antonfirsov and others added 4 commits March 21, 2023 20:05
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
Co-authored-by: Stephen Toub <stoub@microsoft.com>
@antonfirsov
Copy link
Member

/azp run runtime-extra-platforms

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@antonfirsov
Copy link
Member

antonfirsov commented Mar 24, 2023

All feedback has been addressed, and all CI failures are unrelated. Will merge this if there are no further suggestions.

@antonfirsov antonfirsov merged commit 2ae682e into main Mar 27, 2023
@jkotas jkotas deleted the feature/introduce-System.Net.IPNetwork branch March 31, 2023 21:34
@ghost ghost locked as resolved and limited conversation to collaborators May 1, 2023
@karelz karelz added this to the 8.0.0 milestone May 27, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants