Split from https://github.com/dotnet/corefx/issues/26368
```C#
namespace System
{
public static partial class MemoryExtensions
{
public static bool TryCast
public static bool TryCast
}
}
We should add some TryCast extension methods that behave similar to the [NonPortableCast](https://github.com/dotnet/corefx/issues/26368) functions but that include checking beforehand to make sure the operation will be succeed with a usable Span result. To do this, they will need a structure somewhat like this:
if (Platform detect intel)
{
// do NonPortableCast
// return true;
}
else
{
// check the alignment manually.
if ( we're already aligned)
{
// do NonPortableCast
return true;
}
else
{
//do nothing
return false;
}
}
```
From https://github.com/dotnet/corefx/issues/26644:
Consider turning it into an instance method. @mgravell, @benaadams
Question: The T in Span/ReadOnlySpan is not constrained to struct. What happens if I try to cast from SpanC#
public readonly ref partial struct ReadOnlySpan<T>
{
public static bool TryCast<TTo>(out ReadOnlySpan<TTo> output) where TTo : struct;
}
public readonly ref partial struct Span<T>
{
public static bool TryCast<TFrom, TTo>(out Span<TTo> output) where TTo : struct;
}
Personally I'd be fine with a runtime NotSupportedException with a useful message. That makes perfect sense to me.
Could also be persuaded to go for InvalidOperationException...
Makes sense.
Or InvalidCastException ?
I believe the consensus from the review was that we would return false and not throw.
What determines whether "we're already aligned"? If T is a sequential struct, for example, do we have to do the same calculation that the runtime already does to determine its alignment requirements? (Including taking into the account the Pack attribute?) If we're taking into the account the running CPU architecture as the example suggests, is there a list of supported CPU's and their alignment requriments somewhere?
Per Ian, this shouldn't actually be api-approved currently given the post-review discussions.
Possibly just being silly (wouldn't put it past me...) but a possibly solution to @mgravell dislike of having to provide the _from_ and _to_ types (other than adding the functions directly onto Span) might be to do something along the lines of:
static class SpanCasting
{
public ref struct SpanCaster<T>
where T : struct
{
private Span<T> _span;
public SpanCaster(Span<T> span) { _span = span; }
public Span<TTo> Cast<TTo>() where TTo : struct { return _span.NonPortableCast<T, TTo>(); }
public bool Try<TTo>(out Span<TTo> span) where TTo : struct { ...; }
}
public static SpanCaster<T> NonPortable<T>(this Span<T> span)
where T : struct
{
return new SpanCaster<T>(span);
}
}
Which would mean its usage would be something like:
var span = new Span<char>();
var casted1 = span.NonPortable().Cast<uint>();
if (span.NonPortable().Try<uint>(out var casted2)) { ...}
@stephentoub is this API still on the table? I know 3.0 is the priority right now, but perhaps for 3.1?
Rather than TryCast, or perhaps in addition, it would useful to have a bool IsAligned<TFrom,TTo> method on MemoryMarshall which would return whether a given Span is aligned to the TTo type. That could be used implement the same scenarios.
E.g. to implement this code https://github.com/dotnet/corefx/issues/26368#issuecomment-359039294, we could do this:
Span<int> ints;
if(MemoryMarshal.IsAligned<byte, int>(bytes)) {
ints = MemoryMarshal.Cast<byte, int>(bytes);
} else {
ints = new int[bytes.Length/sizeof(int)];
bytes.Slice(0, ints.Length * sizeof(int)).CopyTo(ints.AsBytes());
}
However, if we had IsAligned then the code to implement TryCast would be trivial to achieve:
public static bool TryCast<TFrom,TTo>( Span<TFrom> span, out Span<TTo> outSpan )
{
if (!IsAligned<TFrom,TTo>(span)) {
outSpan = default;
return false;
}
outSpan = Cast<TFrom,TTo>(span);
return true;
}
Should this issue be modified to track the addition of an IsAligned or GetAlignment API? Since the Span<T>.Cast method was removed before shipping and replaced with MemoryMarshal.Cast, I don't think there's much of an appetite to add the API that this issue originally proposed.
Most helpful comment
Or InvalidCastException ?