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

3
  • 4
    is BinaryReader not enough? Commented Jul 30, 2012 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. Commented Jul 30, 2012 at 18:13
  • possible duplicate of A C# equivalent of C's fread file i/o Commented Aug 1, 2012 at 17:01

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

1 Comment

I have already have all functions like ReadShort, ReadShortArray etc. etc.. Just wanted to simplify it more.
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()));
}

3 Comments

This does not really answer the question. If you have a different question, you can ask it by clicking Ask Question. You can also add a bounty to draw more attention to this question. - From Review
@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).
@Ares now that C# 7.3 has been released, I hope you'll reconsider!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.