9

Okay the basic idea what I'm trying to do is, converting byte array to something like short or int etc. etc.

A simple example might be:

        unsafe
        {
            fixed (byte* byteArray = new byte[5] { 255, 255, 255, 126, 34 })
            {
                short shortSingle = *(short*)byteArray;
                MessageBox.Show((shortSingle).ToString()); // works fine output is -1
            }
        }

Okay, so what I'm really trying to do is, make an extension to Stream class; extended read and write methods. I need help at the following code:

unsafe public static T Read<T>(this Stream stream)
        {
            int bytesToRead = sizeof(T); // ERROR: Cannot take the address of, get the size of, or declare a pointer to a managed type ('T')
            byte[] buffer = new byte[bytesToRead];
            if (bytesToRead != stream.Read(buffer, 0, bytesToRead))
            {
                throw new Exception();
            }
            fixed (byte* byteArray = buffer)
            {
                T typeSingle = *(T*)byteArray; // ERROR: Cannot take the address of, get the size of, or declare a pointer to a managed type ('T')
                return typeSingle;
            }
        }

        unsafe public static T[] Read<T>(this Stream stream, int count)
        {
             // haven't figured out it yet. This is where I read and return T arrays
        }

I feel like I have to use pointers for speed because I will be working on writting and reading data from streams like NetworkStream classes. Thanks for your help!

EDIT:

And while I try to figure out how may I return T arrays, I've faced with this problem:

unsafe
        {
            fixed (byte* byteArray = new byte[5] { 0, 0, 255, 255, 34 })
            {
                short* shortArray = (short*)byteArray;
                MessageBox.Show((shortArray[0]).ToString()); // works fine output is 0
                MessageBox.Show((shortArray[1]).ToString()); // works fine output is -1

                short[] managedShortArray = new short[2];
                managedShortArray = shortArray; // The problem is, How may I convert pointer to a managed short array? ERROR: Cannot implicitly convert type 'short*' to 'short[]'
            }
        }

THE SUMMARY: I have to convert from byte array to given type of T OR to given type of T array with given length

haxxoromer
  • 377
  • 2
  • 4
  • 12
  • 4
    is [BinaryReader](http://msdn.microsoft.com/en-us/library/system.io.binaryreader.aspx) not enough? – L.B Jul 30 '12 at 18:04
  • I don't know I'm just, I want to do it myself and try to figure out how this could have done. – haxxoromer Jul 30 '12 at 18:13
  • possible duplicate of [A C# equivalent of C's fread file i/o](http://stackoverflow.com/questions/1935851/a-c-sharp-equivalent-of-cs-fread-file-i-o) – Hans Passant Aug 01 '12 at 17:01

2 Answers2

5

You can't make this function generic because of pointer restrictions in C#. Any of the following types may be a pointer type:

  • sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, or bool.
  • Any enum type.
  • Any pointer type.
  • Any user-defined struct type that contains fields of unmanaged types only.

But you can't set a restriction on T where T <can be pointer type>. where T : struct is very close, but not enough, because user-defined structs can contain fields of reference types.

There is a workaround - System.Runtime.InteropServices.Marshal.PtrToStructure() (it simply throws an exception if it is unable to work with specified object type), but it would also kill any achieved performance improvements.

I think the only way to do this is to create non-generic functions for all desired types.

max
  • 33,369
  • 7
  • 73
  • 84
  • I have already have all functions like ReadShort, ReadShortArray etc. etc.. Just wanted to simplify it more. – haxxoromer Jul 30 '12 at 18:19
1

Edit: unmanaged constraint added to C# 7.3.

Jumping in a bit late on this one, but with C# 7.3 comes the addition of the unmanaged type constraint.

With the unmanaged type constraint, you can use generic pointers (T*) among other things, provided the type passed in is unmanaged.

I tested the generic method you provided, and it does work now. Additionally, you can extend it to return an array like so:

public static unsafe T[] ReadAsArray<T>(this Stream stream) where T : unmanaged
{
    var length = stream.Length;
    var returnArray = new T[length];

    for (var i = 0; i < length; i++)
    {
        int bytesToRead = sizeof(T); // no longer throws error
        byte[] buffer = new byte[bytesToRead];
        if (bytesToRead != stream.Read(buffer, 0, bytesToRead))
        {
            throw new Exception();
        }
        fixed (byte* byteArray = buffer)
        {
            T typeSingle = *(T*)byteArray; // no longer throws error
            returnArray[i] = typeSingle;
        }
    }

    return returnArray;
}

You can call it with the following code, which will print the contents of a file:

using (var sourceStream = File.Open(filename, FileMode.Open))
{
    var byteArray = sourceStream.ReadAsArray<byte>();
    Console.Write(new string(byteArray.Select(b => (char)b).ToArray()));
}
charliefox2
  • 347
  • 3
  • 14
  • This does not really answer the question. If you have a different question, you can ask it by clicking [Ask Question](https://stackoverflow.com/questions/ask). You can also [add a bounty](https://stackoverflow.com/help/privileges/set-bounties) to draw more attention to this question. - [From Review](/review/low-quality-posts/19199837) – Athena Mar 22 '18 at 19:30
  • @Ares I respectfully disagree. If this feature is added to C# 7.3 (it is a champion), appending the constraint `where T : unmanaged` would allow the existing code to compile and function as intended (at least, that's my understanding of the proposal). I recognize that this feature is not _currently_ in the language, but it still feels like a helpful bit of information (to me, at least). – charliefox2 Mar 22 '18 at 19:44
  • @Ares now that C# 7.3 has been released, I hope you'll reconsider! – charliefox2 May 07 '18 at 20:01