Click here to Skip to main content
Click here to Skip to main content

Generic BinaryReader and BinaryWriter Extensions

By , 26 Feb 2009
 
A view from .NET Reflector

Introduction

A while ago, I was working on an application that needed to convert between structs and byte[]. I had many different kinds of structs that I needed to work with, and at first, I tried to maintain a large number of methods for each struct type. You could imagine how bloated the code was.

Eventually, I decided I would give a crack at making all of that conversion code generic, and this miniature project was born. The project provides a static class (like BitConverter) and extension methods for both BinaryReader and BinaryWriter to enable conversion between structs (of any kind) and a series of bytes as an Array or Stream.

Getting Started: The Impossible Way

Since I was somewhat familiar with C# already, naturally I would start the project in C# as well. To start it off, I wrote some code for one of the two conversion methods (shown below) and hit compile:

public static unsafe byte[] GetBytes<T>(T value)
{
    // Check to see if the input is null.
    if (value == null)
    {
        throw new ArgumentNullException();
    }
 
    // Check to see if the input has value type semantics.
    if (!(value is ValueType))
    {
        throw new ArgumentException();
    }
 
    // Create an array of bytes the same size as the input.
    byte[] bytes = new byte[sizeof(T)];
 
    // Pin the byte array down so the garbage collector can't move it.
    fixed (byte* p1 = &bytes[0])
    {
        // Cast the pointer type from byte to T.
        T* p2 = (T*)p1;
 
        // Assign the value to the byte array.
        *p2 = value;
    }
 
    // Return the byte array.
    return bytes;
}

Instead of compiling successfully, Visual Studio gave me an error: “Cannot take the address of, get the size of, or declare a pointer to a managed type ('T').” This was logical. After all, T was not guaranteed to have value type semantics; you can't take the size of something that doesn't have a definite size, and a class can have any size during runtime unless it happens to derive from System.ValueType (e.g. Byte, Int32, UInt64, Single, Decimal, DateTime…). The next logical step would be to force the input to be a ValueType. I removed the error checking code and constrained the type parameter to ValueType:

public static unsafe byte[] GetBytes<T>(T value) where T : ValueType
{
    // Create an array of bytes the same size as the input.
    byte[] bytes = new byte[sizeof(T)];
 
    // Pin the byte array down so the garbage collector can't move it.
    fixed (byte* p1 = &bytes[0])
    {
        // Cast the pointer type from byte to T.
        T* p2 = (T*)p1;
 
        // Assign the value to the byte array.
        *p2 = value;
    }
 
    // Return the byte array.
    return bytes;
}

After hitting the compile key, the error that Visual Studio gave me this time baffled me: “Constraint cannot be special class 'System.ValueType'.” Upon further research, I came upon an MSDN article that suggested constraining the type parameter to a struct. The constraint was accepted by the compiler, but again, it complained that taking the address, pointer, or size of a generic type was not allowed. (Smart compiler, eh?) This, to me, seemed like a dead end. How else was I supposed to write this method?

The C++/CLI Way

I also happened to be playing around with C++/CLI in Visual Studio at around the same time, when an idea hit me. If C++/CLI could do so many things that C# could not (like mixing managed code with unmanaged code), could it let me get around both restrictions in C#? There was only one way to find out, and after an hour of reading C++/CLI tutorials, I came up with this:

generic <typename T>
where T : System::ValueType
array<unsigned char> ^GenericBitConverter::GetBytes(T value)
{
       array<unsigned char> ^bytes = gcnew array<unsigned char>(sizeof(T));
       *reinterpret_cast<interior_ptr<T>>(&bytes[0]) = value;
       return bytes;
}

The method actually worked, and in just 3 lines of code!

Here is the expanded, more readable version (equivalent to the above, but split up to make the steps more obvious):

generic <typename T>
where T : System::ValueType
array<unsigned char> ^Iku::GenericBitConverter::GetBytes(T value)
{
       // Create an array of bytes the same size as the input.
       array<unsigned char> ^bytes = gcnew array<unsigned char>(sizeof(T));
 
       // Create a pointer to the byte array.
       interior_ptr<unsigned char> ptr1 = &bytes[0];
 
       // Cast the pointer to the same type as the input.
       interior_ptr<T> ptr2 = reinterpret_cast<interior_ptr<T>>(ptr1);
 
       // Assign the value to the byte array.
       *ptr2 = value;
 
       // Return the byte array.
       return bytes;
}

GenericBitConverter

… and that is the GenericBitConverter. It works with any type that is derived from ValueType (a struct), and thus will work with all the basic .NET data types like double and decimal (except string, which is really an object) as well as enumerations and other complex structures.

Making an Extension Method in C++/CLI

Now that the GenericBitConverter was in place, it would be nice if the same functionality were available to the BinaryReader and BinaryWriter classes, so I decided to extend those classes with the same functionality as well. This was not any easier than getting the GenericBitConverter to work properly. Writing the method was easy at first:

generic <typename T>
where T : System::ValueType
T Iku::IO::BinaryReaderExtension::ReadValue(BinaryReader ^binaryReader)
{
       return GenericBitConverter::ToValue<T>
		(binaryReader->ReadBytes(sizeof(T)), 0);
}

It just would not show up as an extension method in the C# code editor.

It was time to investigate why, so I wrote one in C# and I disassembled the assembly. While inspecting the output, I found the ExtensionAttribute attribute applied to three places: the assembly, the static class containing the extension method, and the extension method itself. Applying the attributes to the proper place in the C++/CLI project, I was able to get the extension method to show up:

Extension method for BinaryWriter

… and there are the generic BinaryReader and BinaryWriter extensions.

Now you can do stuff like this:

DateTime timeStamp = binaryReader.ReadValue<DateTime>();
static void Main(string[] args)
{
    SampleEnumeration sampEnum = SampleEnumeration.All ^ SampleEnumeration.Four;

    byte[] enumBytes = GenericBitConverter.GetBytes<SampleEnumeration>(sampEnum);

    // Writes "f0 ff ff ff ff ff ff ff"
    foreach (byte bite in enumBytes)
    {
        Console.Write("{0:x2} ", bite);
    }

    Console.WriteLine();
}

enum SampleEnumeration : long
{
    None = 0x0000000000000000,
    All = -1L,
    One = 0x0000000000000001,
    Four = 0x000000000000000f
}

Neat? Feel free to use this within your own C# or Visual Basic projects (by referencing the output of this project). It works perfectly!

Points of Interest

  • C++/CLI also lets you constrain generic parameters to the ValueType type; it does not complain. C# just demands struct in place of ValueType.
  • The C++/CLI unsigned char type is actually a synonym for the Byte type and not the Char type. The C++/CLI Char type is actually wchar_t.
  • Interior pointers (interior_ptr) point to their objects, but do not fix them in place. This is markedly different from C#’s fixed behavior, which fixes the object in memory and prevents the garbage collector from moving it (as long as the fixed statement is in scope).
  • Although C++/CLI does not offer any syntactic sugar for creating extension methods, it can be done by placing an ExtensionAttribute attribute on the method, its containing class, and its containing assembly. (ExtensionAttribute can be found under the namespace System::Runtime::CompilerServices) This nature of extension methods means it can be made from any language that supports attributes, static classes, and static methods.
  • The C# compiler will assume a generic type can be any regular object even if you constrain it to be a struct (or a ValueType). I have not found a way around it in C# (and thus this project was born).

Updates

  • Feb. 27, 2009: Added a download for the compiled assembly; not everyone has the ability to compile a C++/CLI project (users of the Express Editions come to mind). Now everyone can use it!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Kevin Li (Li, Ken-un)
Other
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
BugCLI runtime may rearrange fields.memberZergusik5 Feb '13 - 10:32 
CLI controls the layout of managed types in its own way.
 
So inter-process serialization-deserialization may fail.
 
Even if different applications reuse the same assembly with serialized structures, on different architectures (x32/x64) they may have different in-memory layouts of these structures.
 
However, there is StructLayout attribute, which can control the layout of types in memory.
But even if you specify LayoutKind.Sequential, you'll get different in-memory field order, if the type is "non-blittable" (see MSDN).
 
So, the only correct generic way is Marshal.StructureToPtr()
QuestionIs there a way to achieve this generic byte converter in VB.NET?memberShimmy Weitzhandler17 Jul '10 - 14:33 
I am in an eagerly desire to accomplish this generic byte converter in VB.NET, is there a way?
Shimi

GeneralMy vote of 5memberShimmy Weitzhandler17 Jul '10 - 14:31 
Great article!
Questionassembly attribute for c++/cli assemblymemberMember 127498925 Feb '10 - 3:59 
You don't mention how to set an assembly attribute for the C++/CLI assembly. Can you explain how to do that?
GeneralC# version (not exact, but equivalent) [modified]membergunters1 Jun '09 - 5:29 
For this generic marshaling you can use GCHandle.Alloc and Marshal.StructureToPtr and the Pinned stuff.
 
This is a small helper class i wrote for a project last year. It is not the exact equivalent of your methods, but that requires only a little work.
 
I am not sure your method works everytime. What happens if a garbage collection occurs just between the assigment of ptr1 and the line with *ptr2 = value? The address of ptr1 will probably not the same anymore. Probably only a 1 in a million fail chance, but it could happen one day. Did you stress test your method?
 
///<summary>
/// Helper class for marshaling generic structures. Structures have to have the
/// attribute: [<c>StructLayout</c>(<see cref="LayoutKind"/>.Sequential, Pack = 1)]
///</summary>
public static class MarshalHelper
{
    ///<summary>
    /// Read a structure of type T from stream
    ///</summary>
    ///<param name="fs"></param>
    ///<typeparam name="T"></typeparam>
    ///<returns></returns>
    public static T Read<T>(Stream fs)
    {
        byte[] buffer = new byte[Marshal.SizeOf(typeof(T))];
        fs.Read(buffer, 0, Marshal.SizeOf(typeof(T)));
        return Read<T>(buffer);
    }
 
    ///<summary>
    /// Read a structure of type T from a byte buffer
    ///</summary>
    ///<param name="buffer"></param>
    ///<typeparam name="T"></typeparam>
    ///<returns></returns>
    public static T Read<T>(byte[] buffer)
    {
        GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        try
        {
            return (T) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
        }
        finally
        {
            handle.Free();
        }
    }
 
    ///<summary>
    /// Write a structure of type T to stream
    ///</summary>
    ///<param name="fs"></param>
    ///<param name="t"></param>
    ///<typeparam name="T"></typeparam>
    public static void Write<T>(Stream fs, T t)
    {
        int tsize = Marshal.SizeOf(typeof(T));
        byte[] buffer = new byte[tsize];
        Write(buffer, t);
        fs.Write(buffer, 0, tsize);
    }
 
    ///<summary>
    /// Write a structure of type T to a byte buffer
    ///</summary>
    ///<param name="buffer"></param>
    ///<param name="t"></param>
    ///<typeparam name="T"></typeparam>
    public static void Write<T>(byte[] buffer, T t)
    {
        GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        try
        {
            Marshal.StructureToPtr(t, handle.AddrOfPinnedObject(), false);
        }
        finally
        {
            handle.Free();
        }
    }
}

 
modified on Monday, June 1, 2009 4:15 PM

GeneralRe: C# version (not exact, but equivalent)memberDaniel Grunwald31 Dec '09 - 10:49 
gunters wrote:
What happens if a garbage collection occurs just between the assigment of ptr1 and the line with *ptr2 = value?

 
The GC will adjust the pointer. interior_pointers are managed pointers: they support pointer arithmetic but are still tracked by the GC. C# doesn't support this kind of pointers at all.
 
Your code:
It's not equivalent. You're using the marshaling layer. chars will be converted to the ANSI encoding, causing data loss with Unicode chars.
sizeof(char) = 2
but Marhal.SizeOf(typeof(char)) = 1!
GeneralPure C# Version? [modified]memberRichard Deeming5 Mar '09 - 6:53 
You were so close with your C# version!
 
This seems to work:
public static unsafe byte[] GetBytes<T>(T value) where T : struct
{
    byte[] bytes = new byte[Marshal.SizeOf(typeof(T))];
    fixed (byte* p1 = &bytes[0])
    {
        var ptr = new IntPtr((void*)p1);
        Marshal.StructureToPtr(value, ptr, false);
    }
    
    return bytes;
}
 
public static unsafe T ToValue<T>(byte[] bytes, int startIndex)
{
    if (null == bytes) throw new ArgumentNullException("bytes");
    if (0 > startIndex || startIndex >= bytes.Length) throw new ArgumentOutOfRangeException("startIndex");
    
    fixed (byte* p1 = &bytes[startIndex])
    {
        var ptr = new IntPtr((void*)p1);
        return (T)Marshal.PtrToStructure(ptr, typeof(T));
    }
}
 
Edit: Never mind - it only seems to work with custom structs. DateTime or enum types throw an ArgumentException on the call to Marshal.SizeOf. D'Oh! | :doh:
 



"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer

modified on Thursday, March 5, 2009 1:05 PM

GeneralRe: Pure C# Version?memberLee, Gun-Woon5 Mar '09 - 18:42 
I'm still tinkering with C#, but the more I play around with C++/CLI, the more possibilities I see. The freedom to tinker with things that you can't tinker with in C#… that is what drives me to explore C++/CLI. Smile | :) Although I have my next project/article almost working in C#, I'm tempted to migrate the project to C++/CLI (or try it in both languages); I had to find ugly workarounds for some things C# prevented me from doing (or made it really hard to).
 


My GUID: ca2262a7-0026-4830-a0b3-fe5d66c4eb1d Smile | :)
 
Now I can Google this value and find all my Code Project posts!

GeneralEfficiency (VM and usage) [modified]memberUser of Users Group1 Mar '09 - 4:17 
One, GetBytes could have an overloaded variant with byte[] parameter rather than continously heap allocated return. It would rather return an integer to indicate size consumed.
 
Two, watch out on assumptions of mixed environments and how this will work across platforms.
 
Three, the technique doesn't help in most useful scenario, dispatch based on types (ToValue could do better usage and inference too if you provided another overload). All of this is solved by C++ compilers and its meta-programming, features CLR and C# compiler is incapable of satisfying (cleanly or otherwise) and then some.
 
[ Would be far better to fix C# compiler and generics... in 2009, you cannot have their generic argument value types compared in so many ways, true advance in backwards managed mentality. Bundle that with uselesness of top-scope 'using', across compilation untis, plus its incapability to do indirection twice over you get a : real toy language ]
 
modified on Sunday, March 1, 2009 10:29 AM

GeneralRe: Efficiency (VM and usage)memberLee, Gun-Woon2 Mar '09 - 11:00 
User of Users Group wrote:
One, GetBytes could have an overloaded variant with byte[] parameter rather than continously heap allocated return. It would rather return an integer to indicate size consumed.

You may be right, but I did it for the sake of consistency. Also, it "feels" nore natural to just work with the return value rather than having to do:
byte[] bytes;
int size;
 
size = GenericBitConverter.GetBytes(out bytes, someStruct);
 
User of Users Group wrote:
Two, watch out on assumptions of mixed environments and how this will work across platforms.

Visual Studio C++/CLI automatically target's the x86 platform. I'm guessing C++/CLI assemblies won't work across platforms anyway. The other possible target platforms are x64 and Itanium. This might change if Mono adds support for C++/CLI.
 
User of Users Group wrote:
Three, the technique doesn't help in most useful scenario, dispatch based on types (ToValue could do better usage and inference too if you provided another overload). All of this is solved by C++ compilers and its meta-programming, features CLR and C# compiler is incapable of satisfying (cleanly or otherwise) and then some.
 
[ Would be far better to fix C# compiler and generics... in 2009, you cannot have their generic argument value types compared in so many ways, true advance in backwards managed mentality. Bundle that with uselesness of top-scope 'using', across compilation untis, plus its incapability to do indirection twice over you get a : real toy language ]

We're still talking about C# as it is right now. Smile | :)
 

By the way, I think the univoter (the guy who votes 1 on people's posts) is stalking you. Your posts seem to turn out grey when I encounter them. Unsure | :~
 


My GUID: ca2262a7-0026-4830-a0b3-fe5d66c4eb1d Smile | :)
 
Now I can Google this value and find all my Code Project posts!

GeneralRe: Efficiency (VM and usage) [modified]memberUser of Users Group3 Mar '09 - 12:56 
It is just an overload, so your interface remains more natural but you provide the user with an option (less natural, but even better performance). But I agree with you too. Btw, framework design guidelines from Brad and co has this type of empty discussion we need to read and then unlearn everything they preach if one is to move out of OO-chains and into HPC. Their assumptions are on avoiding interfaces, etc and plenty of other advice, which is clearly essential in any useful C# generics and communication scenarios; one where this and all other generic techniques start to make a bit of sense. Doh!
 
Also another advantage is at least a reduction of one type specification for the case of ToValues if IIRC.
 
> Visual Studio C++/CLI automatically target's the x86 platform
 
Not necessarily, try compiling while running on x64 OS and you could easily hit an exception immediatelly against a specific x86 build. But that is more to do with image format though. The default is 'Any CPU' config, and LLVM or whatever the model makes it safer.. but go across implementations it is an unknown land.
 
You can force these corflags in PE header however. Anyway, there are many scenarios where it won't work cross platform without some manual verification and tweaking, including being very careful with types and its members and attributes etc. On Itanium this code in native form would crash it outright but for CLI I am not sure..
 
I was more aiming at native interop, more later.
 
> We're still talking about C# as it is right now. By the way, I think the univoter (the guy who votes 1 on > people's posts) is stalking you. Your posts seem to turn out grey when I encounter them.
 
[Yeah, we are friends really. He is the guy that does what 99% of other dogs on the street do, chase trains, cars and walks proud every day at how advanced cee-sharper SmallTalk/Java lego toy is (35 years later and the handicap compiler still around and VM and framework more bloated than ever; not to mention a crap JIT and 3.5 memory lang.object-everything eater).]
 
There is more to it though. If you do interop with native C++, you better know what you are doing TM. Including understanding why your compiler pads in thousands of scenarios, alignment, sizeof assumptions, inheritance, you name it. Then there is a VM doing its own thing, and reserves all rights to change.. So even for interop using interop, MS makes sure you stay alert and locked-in.. Too many things to mention, but I agree it is controllable.. and doing this is about as safe as you can be.
 
Multi-dispatch is another story.. (something C# will see in v48.0, done efficiently that is).
 
Btw, I didn't vote on the article..
 
modified on Tuesday, March 3, 2009 7:02 PM

GeneralNotememberUser of Users Group1 Mar '09 - 3:59 
For 'feel free to use', might be worth mentioning how this affects verifiability.
GeneralRe: NotememberLee, Gun-Woon2 Mar '09 - 11:03 
User of Users Group wrote:
For 'feel free to use', might be worth mentioning how this affects verifiability.

You mean, like the fact that the assembly is marked so verification is skipped for it?
 


My GUID: ca2262a7-0026-4830-a0b3-fe5d66c4eb1d Smile | :)
 
Now I can Google this value and find all my Code Project posts!

GeneralRe: NotememberUser of Users Group3 Mar '09 - 12:48 
Yes, I would recommend a few simple test runs in a restricted scenario, over network/domain boundaries or in a host like Silverlight etc. I didn't check your CLI compile options though.. Also you can pass it to PEVerify and see what it comes up with..
JokeGood job.memberdevace1 Mar '09 - 2:49 
I am very proud of you.
I have not seen a article written by korean developer at THE CODE PROJECT main page.
Especially Editor's choice corner is one of top positions.
This reply is written before reading.
GeneralRe: Good job.memberLee, Gun-Woon2 Mar '09 - 11:08 
devace wrote:
I am very proud of you.
I have not seen a article written by korean developer at THE CODE PROJECT main page.
Especially Editor's choice corner is one of top positions.
This reply is written before reading.

I noticed the little joke icon next to the thread title. Should I assume you're serious? Wink | ;)
 


My GUID: ca2262a7-0026-4830-a0b3-fe5d66c4eb1d Smile | :)
 
Now I can Google this value and find all my Code Project posts!

Questionbroken link?memberKenan E.K.27 Feb '09 - 7:25 
The link to the source doesn't seem to be working.
 
Great little gem otherwise.
 
"Have you heard about the object-oriented way to become wealthy?"
"No..."
"Inheritance."

AnswerRe: broken link?memberLee, Gun-Woon27 Feb '09 - 7:58 
The link works fine from my laptop and from other places I've checked. Are you sure you are not having connectivity problems? I only experience the same problem at my employer's site where all ZIP files are blocked by an internet filter.
 


My GUID: ca2262a7-0026-4830-a0b3-fe5d66c4eb1d Smile | :)
 
Now I can Google this value and find all my Code Project posts!

GeneralRe: broken link?memberKenan E.K.27 Feb '09 - 12:47 
Seems to work now.. D'Oh! | :doh:
Probably some firefox caching issue...
 
Well, cheers! Smile | :)
 
"Have you heard about the object-oriented way to become wealthy?"
"No..."
"Inheritance."

GeneralRe: broken link?memberLee, Gun-Woon27 Feb '09 - 17:01 
Or it could have been Code Project. It's been loading slowly for me lately and dropping connections. One of these days, I'm going to lose my work because of a failed connection. Frown | :(
 


My GUID: ca2262a7-0026-4830-a0b3-fe5d66c4eb1d Smile | :)
 
Now I can Google this value and find all my Code Project posts!

GeneralA very good onememberAnandChavali27 Feb '09 - 1:03 
Thanks for sharing (the power of C++ (CLI))
 
Thanks and Regards,
Anand.

AnswerRe: A very good onememberLee, Gun-Woon27 Feb '09 - 4:28 
Thanks for the feedback. And if you are really interested in the power of C++/CLI, check out this part of Nishant Sivakumar's book: Using Interior and Pinning Pointers[^]. You can manipulate strings in memory, something that is illegal (and for very good reasons).
 


My GUID: ca2262a7-0026-4830-a0b3-fe5d66c4eb1d Smile | :)
 
Now I can Google this value and find all my Code Project posts!

GeneralExcellentmemberGSerjo26 Feb '09 - 22:00 
I’ve converted struct to byte[]. It’s really hard Smile | :) . Thanks a lot for the article.
AnswerRe: ExcellentmemberLee, Gun-Woon27 Feb '09 - 4:33 
You're welcome. I was a bit elated too when I saw what I had done. Smile | :) I wish C# could be just as cool.
 


My GUID: ca2262a7-0026-4830-a0b3-fe5d66c4eb1d Smile | :)
 
Now I can Google this value and find all my Code Project posts!

GeneralRe: Excellentmemberapynes27 Feb '09 - 7:43 
Why not just attribute the struct/class as serializable and use a BinaryFormatter?
 
i.e. (C#)
 
        public static byte[] GetBytes(object foo) {
            byte[] bytes;
            using (MemoryStream stream = new MemoryStream()) {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(stream, foo);
                bytes = stream.ToArray();
            }
            return bytes;
        }
 
You could check the attributes of foo here as well to make sure it is marked as [Serializable].
AnswerRe: ExcellentmemberLee, Gun-Woon27 Feb '09 - 8:09 
Although serialization always works, it's just a last resort solution. It's slower and uses more memory. Besides, serialization produces suboptimal results.
 
Does that answer yuor question?
 

I'll put a project together to try to check out the performance of your approach versus mine.
 


My GUID: ca2262a7-0026-4830-a0b3-fe5d66c4eb1d Smile | :)
 
Now I can Google this value and find all my Code Project posts!

GeneralRe: Excellentmemberapynes27 Feb '09 - 9:21 
I have used .Net Remoting in several enterprise level applications, as well as binary serialization to the file system in a directory based queue, all with very satisfactory success and performance (memory usage and processor utilization wise). BinarySerialization is how .NET passes objects between app domains as well as in remoting. Also the binary serialization allows for not just structs, but classes as well, to be transmitted. As a bonus it also allows you to explicitly decide what will be included or excluded.
 
Perhaps your sol'n is optimal for your particular usage, I was just pointing out that C# can produce the same results you are seeking. i.e. you don't have to resort to a different language to get the binary representation of an object.
 
I would imagine that most engineering departments would consider adding C++/CLI code to a primarily C# sol'n for a single method to be a 'last resort sol'n'.
AnswerRe: ExcellentmemberLee, Gun-Woon27 Feb '09 - 12:32 
apynes wrote:
I have used .Net Remoting in several enterprise level applications, as well as binary serialization to the file system in a directory based queue, all with very satisfactory success and performance (memory usage and processor utilization wise). BinarySerialization is how .NET passes objects between app domains as well as in remoting. Also the binary serialization allows for not just structs, but classes as well, to be transmitted. As a bonus it also allows you to explicitly decide what will be included or excluded.

You're absolutely right, but look at the source of inspiration for the GenericBitConverter, BinaryReader, and BinaryWriter extensions. It was never meant for serialization in the first place. The GenericBitConverter, BinaryReader and BinaryWriter are there when you want to read or write an Int32 and expect to work with 4 bytes instead of those 4 bytes plus some extra bookkeeping data. This solution provides a way to persist any type in a compact format (zero overhead) and with only one line of code versus the possibly n lines of code you would have to write for a struct with n members.
 
apynes wrote:
Perhaps your sol'n is optimal for your particular usage, I was just pointing out that C# can produce the same results you are seeking. i.e. you don't have to resort to a different language to get the binary representation of an object.

Here is a situation I can think of:
Reading and writing file formats that were not meant to be strictly used in a .NET environment (although a series of calls to methods in the BinaryReader and BinaryWriter methods accomplish the same thing, albeit less elegantly and with more boilerplate code).
 
apynes wrote:
I would imagine that most engineering departments would consider adding C++/CLI code to a primarily C# sol'n for a single method to be a 'last resort sol'n'.

Generally correct, but in this case I thought this was the best solution versus the other solutions (e.g., .NET serialization, a series of calls to BitConverter, BinaryReader, or BinaryWriter to read or write the struct). I did say in the introduction that I had to work with a lot of different structs… I just didn't want to maintain a pair of methods for each kind of struct or break compatibility by using .NET serialization.
 

Any more remarks? Smile | :)
 


My GUID: ca2262a7-0026-4830-a0b3-fe5d66c4eb1d Smile | :)
 
Now I can Google this value and find all my Code Project posts!

GeneralRe: ExcellentmemberUser of Users Group28 Feb '09 - 1:16 
> Any more remarks?
 
Agree with both of you really..
 
We used a similar technique to circumvent the poor generic facilities of a mass-market toy called C#.
 
But you still have 2 messages in your inbox Smile | :)
JokeRe: ExcellentmemberShimmy Weitzhandler17 Jul '10 - 14:32 
"My GUID: ca2262a7-0026-4830-a0b3-fe5d66c4eb1d "
 
You'll find all these comments including mine too...
Shimi

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 27 Feb 2009
Article Copyright 2009 by Kevin Li (Li, Ken-un)
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid