diff --git a/csharp/src/Apache.Arrow/Arrays/BinaryArray.cs b/csharp/src/Apache.Arrow/Arrays/BinaryArray.cs index 20fe0342cca40..a7ddb14af6a10 100644 --- a/csharp/src/Apache.Arrow/Arrays/BinaryArray.cs +++ b/csharp/src/Apache.Arrow/Arrays/BinaryArray.cs @@ -331,20 +331,33 @@ public int GetValueLength(int index) /// /// Note that this method cannot reliably identify null values, which are indistinguishable from empty byte /// collection values when seen in the context of this method's return type of . - /// Use the method instead to reliably determine null values. + /// Use the method or the overload instead + /// to reliably determine null values. /// /// Index at which to get bytes. /// Returns a object. /// If the index is negative or beyond the length of the array. /// - public ReadOnlySpan GetBytes(int index) + public ReadOnlySpan GetBytes(int index) => GetBytes(index, out _); + + /// + /// Get the collection of bytes, as a read-only span, at a given index in the array. + /// + /// Index at which to get bytes. + /// Set to if the value at the given index is null. + /// Returns a object. + /// If the index is negative or beyond the length of the array. + /// + public ReadOnlySpan GetBytes(int index, out bool isNull) { if (index < 0 || index >= Length) { throw new ArgumentOutOfRangeException(nameof(index)); } - if (IsNull(index)) + isNull = IsNull(index); + + if (isNull) { // Note that `return null;` is valid syntax, but would be misleading as `null` in the context of a span // is actually returned as an empty span. @@ -353,6 +366,5 @@ public ReadOnlySpan GetBytes(int index) return ValueBuffer.Span.Slice(ValueOffsets[index], GetValueLength(index)); } - } } diff --git a/csharp/src/Apache.Arrow/Arrays/StringArray.cs b/csharp/src/Apache.Arrow/Arrays/StringArray.cs index f008f56fa8477..42104b27175a9 100644 --- a/csharp/src/Apache.Arrow/Arrays/StringArray.cs +++ b/csharp/src/Apache.Arrow/Arrays/StringArray.cs @@ -72,11 +72,11 @@ public StringArray(int length, public string GetString(int index, Encoding encoding = default) { - encoding = encoding ?? DefaultEncoding; + encoding ??= DefaultEncoding; - ReadOnlySpan bytes = GetBytes(index); + ReadOnlySpan bytes = GetBytes(index, out bool isNull); - if (bytes == default) + if (isNull) { return null; } diff --git a/csharp/test/Apache.Arrow.Tests/BinaryArrayBuilderTests.cs b/csharp/test/Apache.Arrow.Tests/BinaryArrayBuilderTests.cs index 7f45ce8578fc3..4c2b050d0c8ba 100644 --- a/csharp/test/Apache.Arrow.Tests/BinaryArrayBuilderTests.cs +++ b/csharp/test/Apache.Arrow.Tests/BinaryArrayBuilderTests.cs @@ -481,7 +481,8 @@ private static void AssertArrayContents(IEnumerable expectedContents, Bi for (int i = 0; i < array.Length; i++) { var expectedArray = expectedContentsArr[i]; - var actualArray = array.IsNull(i) ? null : array.GetBytes(i).ToArray(); + var actualSpan = array.GetBytes(i, out bool isNull); + var actualArray = isNull ? null : actualSpan.ToArray(); Assert.Equal(expectedArray, actualArray); } } diff --git a/csharp/test/Apache.Arrow.Tests/StringArrayTests.cs b/csharp/test/Apache.Arrow.Tests/StringArrayTests.cs new file mode 100644 index 0000000000000..0fd3d3d105a70 --- /dev/null +++ b/csharp/test/Apache.Arrow.Tests/StringArrayTests.cs @@ -0,0 +1,54 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Xunit; + +namespace Apache.Arrow.Tests +{ + public class StringArrayTests + { + public class GetString + { + [Theory] + [InlineData(null, null)] + [InlineData(null, "")] + [InlineData(null, "value")] + [InlineData("", null)] + [InlineData("", "")] + [InlineData("", "value")] + [InlineData("value", null)] + [InlineData("value", "")] + [InlineData("value", "value")] + public void ReturnsAppendedValue(string firstValue, string secondValue) + { + // Arrange + // Create an array with two elements. The second element being null, + // empty, or non-empty may influence the underlying BinaryArray + // storage such that retrieving an empty first element could result + // in an empty span or a 0-length span backed by storage. + var array = new StringArray.Builder() + .Append(firstValue) + .Append(secondValue) + .Build(); + + // Act + var retrievedValue = array.GetString(0); + + // Assert + Assert.Equal(firstValue, retrievedValue); + } + } + } +}