Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Stream.Write not take a UInt?

It seems highly illogical to me that Stream.Write uses int, instead of UInt... Is there an explanation other than "legacy" code for this fact? Would any one want to write -1 bytes?!?

like image 574
Leonardo Avatar asked Jun 01 '15 18:06

Leonardo


2 Answers

Unsigned types are not CLS-compliant, hence Stream.Write doesn't use uint for offset and count.

See: uint (C# Reference)

The uint type is not CLS-compliant. Use int whenever possible.

There is an old article: Why we don't have unsigned types in the CLS by Brad Abrams (2 Sep 2003) that explains the reason:

However there is one issue that keeps coming up: Why did we not allow unsigned types (UInt32 and the like) in the CLS?

Well, there are really two answers to this question. At the first level some languages (such as VB.NET) do not offer full support for unsigned types. For example you can’t have unsigned literals in VB.NET…. But to be fair that is not a completely satisfying answer because when we started the CLS you could not subclass in VB.NET either, but we extended that language to support what we knew people would want. We could have done the same thing with unsigned types. But we didn’t. Why not? Well, that gets a deeper reason. In fact the same reason why early betas of the C# language did not support unsigned types (no ushort, uint and the like).

The general feeling among many of us is that the vast majority of programming is done with signed types. Whenever you switch to unsigned types you force a mental model switch (and an ugly cast). In the worst cast you build up a whole parallel world of APIs that take unsigned types. The value of avoiding the “< 0” check is not worth the inclusion of generics in the CLS.

(Note that newer version of VB.Net (VB 8 onwards) supports unsigned types).

One more thing (probably unrelated) to add, Stream.Write implementation has checks for negative values:

[System.Security.SecuritySafeCritical]  // auto-generated
public override void Write(byte[] array, int offset, int count) {
    if (array==null)
        throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Buffer"));
    if (offset < 0)
        throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    if (count < 0)
        throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    if (array.Length - offset < count)
        throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
like image 185
Habib Avatar answered Oct 17 '22 15:10

Habib


From the uint MSDN only

The uint type is not CLS-compliant. Use int whenever possible.

So Stream.Write uses int for offset and count.

The reasons given by ShuggyCoUk makes it more clear:

  • uint is not CLS compliant, thus making a built in type (array) dependent on it would have been problematic
  • The runtime as originally designed prohibits any object on the heap occupying more than 2GB of memory. Since the maximum sized array that would less than or equal to this limit would be new byte[int.MaxValue] it would be puzzling to people to be able to generate positive but illegal array lengths.
    • Note that this limitation has been somewhat removed in the 4.5 release, though the standard Length as int remains.
  • Historically C# inherits much of its syntax and convention from C and C++. In those arrays are simply pointer arithmetic so negative array indexing was possible (though normally illegal and dangerous). Since much existing code assumes that the array index is negative this would have been a factor
  • On a related note the use of signed integers for array indexes in C/C++ means that interop with these languages and unmanaged functions would require the use of ints in those circumstances anyway, which may confuse due to the inconsistency.
  • The BinarySearch implementation (a very useful component of many algorithms) relies on being able to use the negative range of the int to indicate that the value was not found and the location at which such a value should be inserted to maintain sorting.
  • When operating on an array it is likely that you would want to take a negative offset of an existing index. If you used an offset which would take you past the start of the array using unit then the wrap around behaviour would make your index possibly legal (in that it is positive). With an int the result would be illegal (but safe since the runtime would guard against reading invalid memory)
like image 5
Rahul Tripathi Avatar answered Oct 17 '22 14:10

Rahul Tripathi