C
C#11mo ago
AndyB

❔ Is `where` still used?

I'm looking at creating some functions to convert byte[] to Structures and back, and all examples I see are very old. They use the keyword "where" which I've never seen before. Is this still current or has it been depricated? e.g.
private static byte[] StructToBytes<T>(T data) where T : struct
{
byte[] rawData = new byte[Marshal.SizeOf(data)];
GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned);
try
{
IntPtr rawDataPtr = handle.AddrOfPinnedObject();
Marshal.StructureToPtr(data, rawDataPtr, false);
}
finally
{
handle.Free();
}

return rawData;
}
private static byte[] StructToBytes<T>(T data) where T : struct
{
byte[] rawData = new byte[Marshal.SizeOf(data)];
GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned);
try
{
IntPtr rawDataPtr = handle.AddrOfPinnedObject();
Marshal.StructureToPtr(data, rawDataPtr, false);
}
finally
{
handle.Free();
}

return rawData;
}
26 Replies
Developful
Developful11mo ago
the where keyword (it's not deprecated) constraints T, in this case, to be a struct. for example I could also constraint T to be part of a class named Node for that, i'd just type where T : Node so if you try to pass in something that is not a struct, it'll give you an error
AndyB
AndyB11mo ago
Copy, TY. So are you saying the code would still work without the where but using it will help in debugging?
Developful
Developful11mo ago
in some cases it would work, in some it wouldn't but no, it wouldnt work
AndyB
AndyB11mo ago
Ok. For some reason, I hadn't seen that keyword before.
Developful
Developful11mo ago
because if you remove the struct constraint, if the code expects a struct anywhere within the functions it wont be sure that T is a struct, and therefore will give you an error
AndyB
AndyB11mo ago
Would you say this is still a valid way to copy bytes into a fixed structure, or are there more modern (.NET 8) ways to do this? Assuming the struct is formed correctly with LayoutKind.Sequential and so on...
jiniux
jiniux11mo ago
you could use a span to avoid the heap allocation of the byte array
AndyB
AndyB11mo ago
Go on...
jiniux
jiniux11mo ago
Stack Overflow
How get a Span view of a struct without the unsafe keyword
How can a Span<byte> view (reinterpret cast) be created from a single struct value with no copying, no allocations, and without the unsafe keyword.
I can currently only accomplish this usi...
jiniux
jiniux11mo ago
keep in mind that a span is a reference to existing memory
AndyB
AndyB11mo ago
TY. It says "partial solution", is this valid code?
using System.Runtime.InteropServices;

public Span<byte> StructToBytes<T>(ref T data) where T : unmanaged
{
Span<T> dataSpan = MemoryMarshal.CreateSpan(ref data, 1);
return MemoryMarshal.Cast<T, byte>(dataSpan);
}
using System.Runtime.InteropServices;

public Span<byte> StructToBytes<T>(ref T data) where T : unmanaged
{
Span<T> dataSpan = MemoryMarshal.CreateSpan(ref data, 1);
return MemoryMarshal.Cast<T, byte>(dataSpan);
}
Hadn't heard of MemoryMarshal before.
jiniux
jiniux11mo ago
for what i understood this works if you're not targeting netstandard2.0
AndyB
AndyB11mo ago
I'm using .NET8
jiniux
jiniux11mo ago
you should be fine
AndyB
AndyB11mo ago
Looks simpler, and no try/catch needed?
jiniux
jiniux11mo ago
nope it's just a cast
AndyB
AndyB11mo ago
Very cool. Just have to use Span<byte> instead of byte[] when reading the data. TY!
jiniux
jiniux11mo ago
though i don't know how safe is this code because i don't know how it is used so make good judgment whether to use byte[] or Span<byte>
AndyB
AndyB11mo ago
When working with hardware or embedded devices, It's a very common part of C/C++ software where you read byte data from a device and then manipulate it with a structure. C# being managed doesn't seem to have this built-in ability, so just looking to create some generic functions. Appreciate the help!
jiniux
jiniux11mo ago
there are many options to use C# in an "unmanaged way", you should take a look to the unsafe keyword, the classes MemoryMarshal, Unsafe etc... so just be careful that span is like a pointer, if the memory it points to is invalid, it will trigger an undefined behavior
Thinker
Thinker11mo ago
It's definitely not deprecated in any way. Type parameter constraints (where T :) are used all around for various purposes, everything from in your example constraining types arguments to only value types, to constraining to types implementing certain interfaces. The numeric interfaces introduced in .NET 7 hinges on constraints to be useful in the first place. Constraints are alive and well. eg.
T Sum<T>(IEnumerable<T> xs) where T : INumber<T>
{
var result = T.Zero;
foreach (var x in xs) result += x;
return result;
}

var ints = new[] { 1, 2, 3 };
Console.WriteLine(Sum(ints)); // 6

var floats = new[] { 1.5f, 2.2f, 0.3f, };
Console.WriteLine(Sum(floats)); // 3.0
T Sum<T>(IEnumerable<T> xs) where T : INumber<T>
{
var result = T.Zero;
foreach (var x in xs) result += x;
return result;
}

var ints = new[] { 1, 2, 3 };
Console.WriteLine(Sum(ints)); // 6

var floats = new[] { 1.5f, 2.2f, 0.3f, };
Console.WriteLine(Sum(floats)); // 3.0
TheBoxyBear
TheBoxyBear11mo ago
And you can safely pass references of T where the constraint type is expected without casting.
AndyB
AndyB11mo ago
Thanks everyone. TIL about “where” ! What about this?
public Span<byte> StructToBytes<T>(ref T data) where T: struct
{
return MemoryMarshal.AsBytes<T>(data);
}
public Span<byte> StructToBytes<T>(ref T data) where T: struct
{
return MemoryMarshal.AsBytes<T>(data);
}
??
Developful
Developful11mo ago
what about this.. what?
hiyosilver
hiyosilver11mo ago
This is some code containing several words and special characters.
Accord
Accord11mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.