How to adopt PolySharp to support sub arrays
According to the documentation PolySharp supports "Index and Range (see indices and ranges)".
The sample of the referenced Microsoft site tries to create sub-arrays:
string[] secondThirdFourth = words[1..4];
Unfortunately, this sample gets complaints:
I'm confused about the generic statement regarding PolySharp's support for "Index and Range". Do I miss something?
What data type is your _words ? I actually had the exact same issue. Turns our this only works on Arrays and Spans, as those are know to the compiler. I tried it with List
Edit: Sorry, correction - I have been trying this with diverse packages as well. My tests:
- this PolySharp package: Your error
- Polyfill package: Same error
- PolyShim package: Works like I described above
- PolyKit: Your error
- Meziantou.Polyfill: Your Error
- No package, using https://www.meziantou.net/how-to-use-csharp-8-indices-and-ranges-in-dotnet-standard-2-0-and-dotn.htm (Look for "You can copy the implementation from this snippet" at the end, download and include that file) works. But that is only this specific part and does not work together with another polyfill package after a quick test, as the classes are then implemented twice.
I cannot say anything about the quality of the PolyShim package, which seems to be the only one where this works 🤔
Will also look more into this, but maybe this helps you :-)
And one more update: Seem to work with this and the other package if you just add that missing method, I got that from the blog link I posted. You only need this class / method
namespace System.Runtime.CompilerServices
{
internal static class RuntimeHelpers
{
/// <summary>
/// Slices the specified array using the specified range.
/// </summary>
public static T[] GetSubArray<T>(T[] array, Range range)
{
if (array == null)
{
throw new ArgumentNullException(nameof(array));
}
(int offset, int length) = range.GetOffsetAndLength(array.Length);
if (default(T) != null || typeof(T[]) == array.GetType())
{
// We know the type of the array to be exactly T[].
if (length == 0)
{
return Array.Empty<T>();
}
var dest = new T[length];
Array.Copy(array, offset, dest, 0, length);
return dest;
}
else
{
// The array is actually a U[] where U:T.
var dest = (T[])Array.CreateInstance(array.GetType().GetElementType(), length);
Array.Copy(array, offset, dest, 0, length);
return dest;
}
}
}
}
Last update (for now). I modified the method a bit to be closer to the original from https://source.dot.net/#System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs. I did some quick tests including casting the array to object[] etc., seems to work. Maybe this helps you :-)
Not sure what the default(T) != null is for in the previously posted version. I can only assume some shortcut for value and struct types that cannot be null and cannot be inherited. May be a quick faster with that all around, I did not benchmark this.
using System.Security.Cryptography;
namespace System.Runtime.CompilerServices
{
internal static class RuntimeHelpers
{
/// <summary>
/// Slices the specified array using the specified range.
/// Adapted from source: https://source.dot.net/#System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs.
/// Required for be able to use `array[10..20] ` syntax in .NET Standard 2.0 and net48.
/// </summary>
public static T[] GetSubArray<T>(T[] array, Range range)
{
if (array == null)
{
throw new ArgumentNullException(nameof(array));
}
(int offset, int length) = range.GetOffsetAndLength(array.Length);
if (length == 0)
{
return Array.Empty<T>();
}
T[] dest;
if (typeof(T[]) == array.GetType())
{
// We know the type of the array to be exactly T[].
dest = new T[length];
}
else
{
// The array is actually a U[] where U:T. We'll make sure to create
// an array of the exact same backing type. The cast to T[] will
// never fail.
dest = (T[])Array.CreateInstance(array.GetType().GetElementType(), length);
}
// In either case, the newly-allocated array is the exact same type as the
// original incoming array. It's safe for us to Array.Copy the contents
// from the source array to the destination array, otherwise the contents
// wouldn't have been valid for the source array in the first place.
Array.Copy(array, offset, dest, 0, length);
return dest;
}
}
}
@Sergio0694 @xilefius why not add this to the code base? ;)
The problem is RuntimeHelpers already exists in netstandard2.0.
Yes, it is possible to overlay the type only for an assembly for compiler use. It cannot be an external library. The problem is that the compiler does not only use the “GetSubArray” method. Depending on the program, the compiler can also use methods like “InitializeArray”, “RunClassConstructor”, “RunModuleConstructor”, “CreateSpan<T>”, “GetObjectValue”, and “GetUninitializedObject”. I don't know if this list is accurate, but if there is really an intention to implement “GetSubArray,” I recommend using reflection for the original methods. Here is a very simplified example: “typeof(object).Assembly.GetType(“System.Runtime.CompilerServices.RuntimeHelpers”, true).GetMethod(“InitializeArray”).Invoke(null, new object[] { array, fldHandle });” Since they are public methods, parameters like BindingFlags can be ignored, but in the case of methods with the same name but different parameters, assigning Type[] is necessary. A production reflection would create delegates for speed. This is a simple example, and it is necessary to get the type from another assembly since there is a type with the same name in the code to compile, overlying the original type. I would be happy to see it implemented, but I'll be honest that it's quite unlikely to happen since it involves some reflection…
Maybe this will be possible with C# 14 because then it will be possible to create static extension methods.
Maybe this will be possible with C# 14 because then it will be possible to create static extension methods.
Sadly, no it isn't. I've tried but the compiler is ignoring the extension for it's "hidden" use. That is it works fine for anything that is using it directly from YOUR code, but the compiler only looks at the RuntimeHelpers type itself (ignoring the work to resolve an extension). If the static method isn't directly on the type, it can't inject a call to that and fails.😭