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

Polyfill instance methods with extensions #6

Open
Tyrrrz opened this issue Oct 24, 2022 · 2 comments
Open

Polyfill instance methods with extensions #6

Tyrrrz opened this issue Oct 24, 2022 · 2 comments

Comments

@Tyrrrz
Copy link

Tyrrrz commented Oct 24, 2022

Description (optional)

First off, awesome idea for the project. I was hoping someone would do it :)

I was wondering if you had any plans to polyfill certain built-in instance methods with extensions.
For example, these are some of the polyfills I have in my projects:

https://github.com/Tyrrrz/CliWrap/blob/ccc3087f0744dce17899a433591b3e50fd18e90b/CliWrap/Utils/Polyfills.Collections.cs#L1-L17

https://github.com/Tyrrrz/CliWrap/blob/ccc3087f0744dce17899a433591b3e50fd18e90b/CliWrap/Utils/Polyfills.Streams.cs#L1-L57

They allow me to seamlessly call methods only available on newer frameworks, while polyfilling the functionality on older frameworks.

Rationale

Besides attributes, this could be an additional aspect of polyfilling that might be worth exploring. As a user, I can see great benefit in this as it would allow me to replace my own polyfills that I copy around between projects with a single package.

Proposed API

I propose a similar approach that I've been using: extensions in a global namespace. This makes the polyfill callsites fully source-compatible with natural callsites.

Drawbacks

This would require to figure out how to actually implement certain polyfills using existing functionality, and might not be trivial in some cases.

Alternatives

Just writing polyfills manually.

Other thoughts

None.

@menees
Copy link

menees commented Oct 30, 2022

I agree. This is a great project, and it would be nice if it also filled in some of the new .NET instance methods via extensions.

I use the Microsoft.CodeAnalysis.NetAnalyzers code analysis rules on projects that target both .NET Framework 4.8 and .NET 6.0. Rule CA2249 recommends using Contains(char) instead of IndexOf(char) >= 0, which is great for .NET 6.0. But I have to add a Contains(char) polyfill for my .NET Framework targets to make this work. It would be great if Poly# provided implementations like these:

/// <summary>
/// Returns a value indicating whether a specified character occurs within this string.
/// </summary>
/// <param name="text">The string to search in.</param>
/// <param name="value">The character to seek.</param>
/// <remarks>This method performs an ordinal (case-sensitive and culture-insensitive) comparison.</remarks>
/// <returns>true if the value parameter occurs within this string; otherwise, false.</returns>
public static bool Contains(this string text, char value)
	=> text.IndexOf(value) >= 0;

/// <summary>
/// Returns a value indicating whether a specified string occurs within this string, using the specified comparison rules.
/// </summary>
/// <param name="text">The string to search in.</param>
/// <param name="value">The string to seek.</param>
/// <param name="comparison">One of the enumeration values that specifies the rules to use in the comparison.</param>
/// <returns>true if the value parameter occurs within this string, or if value is the empty string (""); otherwise, false.</returns>
public static bool Contains(this string text, string value, StringComparison comparison)
	=> text.IndexOf(value, comparison) >= 0;

/// <summary>
/// Determines whether the end of the <paramref name="text"/> string instance matches the specified character.
/// </summary>
/// <param name="text">The text to check.</param>
/// <param name="ch">The character to compare to the character at the end of <paramref name="text"/>.</param>
/// <returns>
/// True if <paramref name="text"/> is non-empty and ends with <paramref name="ch"/>.
/// False if <paramref name="text"/> is null, empty, or doesn't end with <paramref name="ch"/>.
/// </returns>
public static bool EndsWith(this string text, char ch)
	=> text.Length > 0 && text[text.Length - 1] == ch;

/// <summary>
/// Determines whether the start of the <paramref name="text"/> string instance matches the specified character.
/// </summary>
/// <param name="text">The text to check.</param>
/// <param name="ch">The character to compare to the character at the start of <paramref name="text"/>.</param>
/// <returns>
/// True if <paramref name="text"/> is non-empty and starts with <paramref name="ch"/>.
/// False if <paramref name="text"/> is null, empty, or doesn't start with <paramref name="ch"/>.
/// </returns>
public static bool StartsWith(this string text, char ch)
	=> text.Length > 0 && text[0] == ch;

And here are some unit tests using MSTest v2 and Shoudly:

[TestMethod]
public void ContainsChar()
{
	string.Empty.Contains('x').ShouldBeFalse();
	"Test".Contains('e').ShouldBeTrue();
	"Test".Contains('S').ShouldBeFalse();
}

[TestMethod]
public void ContainsString()
{
	string.Empty.Contains("x", StringComparison.OrdinalIgnoreCase).ShouldBeFalse();
	"Test".Contains("es", StringComparison.OrdinalIgnoreCase).ShouldBeTrue();
	"Test".Contains("ES", StringComparison.OrdinalIgnoreCase).ShouldBeTrue();
}

[TestMethod]
public void EndsWith()
{
	string.Empty.EndsWith('x').ShouldBeFalse();
	"Test".EndsWith('t').ShouldBeTrue();
	"Test".EndsWith('T').ShouldBeFalse();
}

[TestMethod]
public void StartsWith()
{
	string.Empty.StartsWith('x').ShouldBeFalse();
	"Test".StartsWith('t').ShouldBeFalse();
	"Test".StartsWith('T').ShouldBeTrue();
}

@cremor
Copy link

cremor commented May 3, 2023

Here are quite a few examples of which polyfill extension methods would be possible: https://github.com/SimonCropp/Polyfill#extensions

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

No branches or pull requests

3 participants