5

Think of the following code:

static int Main() {
     byte[] data = File.ReadAllBytes("anyfile");
     SomeMethod(data);
     ...
}
static void SomeMethod(byte[] data) {
     data[0] = anybytevalue; // this line should not be possible!!!
     byte b = data[0];       // only reading should be allowed
     ...
}

Is there a way of readonly passing the byte[] in C#? Copying isn't a solution. I would not like to waste memory (because the file might get very big). Please keep performance in mind!

John Saunders
  • 160,644
  • 26
  • 247
  • 397
raisyn
  • 4,514
  • 9
  • 36
  • 55

3 Answers3

13

You can pass a ReadOnlyCollection<byte>, like this:

static int Main() {
     byte[] data = File.ReadAllBytes("anyfile");
     SomeMethod(new ReadOnlyCollection<byte>(data));
     ...
}
static void SomeMethod(ReadOnlyCollection<byte> data) {
     byte b = data[0];       // only reading is allowed
     ...
}

However, it would be better to pass a Stream, like this:
This way, you won't read the entire file into memory at all.

static int Main() {
     Stream file = File.OpenRead("anyfile");
     SomeMethod(file);
     ...
}
static void SomeMethod(Stream data) {
     byte b = data.ReadByte();       // only reading is allowed
     ...
}
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • Are there any performance differences when using the ReadOnlyCollection instead of the array? – raisyn Jul 18 '10 at 21:02
  • @youllknow: Not measurably; `ReadOnlyCollection` is a thin wrapper around an `IList`. – SLaks Jul 18 '10 at 21:38
  • @youllknow: As SLaks mentioned, ReadOnlyCollection is actually a wrapper around IList. So accessing an item is a method call *and* an interface call. This is very expensive, compared to the cost of accessing a single byte in an array (like 20x more expensive). If your array is large, be sure to profile this! – Niki Jul 18 '10 at 21:51
  • @nikie, I did some tests, on average it's about 3.5 times slower, not 20 times... – Thomas Levesque Jul 18 '10 at 23:02
  • @Thomas: What did you test? I tested summing a 1.000.000 byte array in a loop. Array: 0.6ms, ReadOnlyCollection: 20 ms. That's a lot more than factor 3.5 – Niki Jul 19 '10 at 07:17
  • I did the same with a 1GB array. I just tried again with a 1MB array, I still get a ratio of about 3.5. – Thomas Levesque Jul 19 '10 at 08:13
  • @nikie, @Thomas: What bitness are you testing on? – SLaks Jul 19 '10 at 12:44
  • @SLaks: Windows 7, 32 bit, Core i7 processor. Do you think that could lead to a difference that large? – Niki Jul 19 '10 at 16:12
  • @nikie:The CLR and JITters are very different for different bitnesses. (Somewhat less so in .Net 4.0) – SLaks Jul 19 '10 at 16:20
  • Thanks very much, do performance reason I will pass the original byte[] instead of getting more code security! – raisyn Jul 19 '10 at 18:49
6

I think this might be what you're looking for.

Compile the code below and you will get this compile error: Property or indexer 'Stack2.MyReadOnlyBytes.this[int]' cannot be assigned to -- it is read only

public class MyReadOnlyBytes
{
   private byte[] myData;

   public MyReadOnlyBytes(byte[] data)
   {
      myData = data;
   }

   public byte this[int i]
   {
      get
      {
         return myData[i];
      }
   }
}

class Program
{
   static void Main(string[] args)
   {
      var b = File.ReadAllBytes(@"C:\Windows\explorer.exe");
      var myb = new MyReadOnlyBytes(b);

      Test(myb);

      Console.ReadLine();
   }

   private static void Test(MyReadOnlyBytes myb)
   {
      Console.WriteLine(myb[0]);
      myb[0] = myb[1];
      Console.WriteLine(myb[0]);
   }
}
Tyler Jensen
  • 811
  • 6
  • 12
2

I would recommend you using the highest possible object in the hierarchy that does the job. In your case this would be IEnumerable<byte>:

static int Main() 
{
     byte[] data = File.ReadAllBytes("anyfile");
     SomeMethod(data);
}

static void SomeMethod(IEnumerable<byte> data)
{
    byte b = data.ElementAt(0); 
    // Notice that the ElementAt extension method is sufficiently intelligent
    // to use the indexer in this case instead of creating an enumerator
}
Steven Sudit
  • 19,391
  • 1
  • 51
  • 53
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • `((byte[])data)[0] = anybytevalue;` – SLaks Jul 18 '10 at 20:59
  • 2
    @Slaks, sure casting, but no matter what you try there will always be ways to circumvent this. For example reflection. So even with a `ReadOnlyCollection` the user will be capable of modifying the original byte structure (though he will need to write a bit more code than simply casting). I assumed that the OP was looking for a compile time safety here and not runtime. – Darin Dimitrov Jul 18 '10 at 21:01
  • Well, you *could* create a copy of the data with Enumerable.ToList. No way to circumvent that with a cast or reflection. I wouldn't recommend it, though. – Niki Jul 18 '10 at 21:54