From 0b443a69d9ef98602bddee9814df6a276516a7f4 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 20 Jan 2026 09:56:29 +0100 Subject: [PATCH 1/2] [Foundation] Unify the FromObjectsAndKeys implementations for creating dictionaries. * Add two validation functions, one for when FromObjectsAndKeys is called with a count, and one for when FromObjectsAndKeys is called without a count. * Call the corresponding validation function in every FromObjectsAndKeys implementation. * In every FromObjectsAndKeys function without a count parameter, forward the implementation to the FromObjectsAndKeys with a count parameter. * Add numerous tests. Consequences: * Specifying count=0 now succeed (creating an empty dictionary). * Fixed numerous cases where the count parameter was ignored. * Allow arrays of different lengths for the key and values array parameters when specifying a count. --- src/Foundation/NSDictionary.cs | 75 +++++---- src/Foundation/NSDictionary_2.cs | 45 ++---- src/Foundation/NSMutableDictionary.cs | 64 +++----- src/Foundation/NSMutableDictionary_2.cs | 56 +++---- .../Foundation/NSDictionary2Test.cs | 11 ++ .../Foundation/NSDictionaryTest.cs | 20 +++ .../Foundation/NSMutableDictionary2Test.cs | 145 ++++++++++++++++++ .../Foundation/NSMutableDictionaryTest.cs | 107 +++++++++++++ .../Foundation/NSMutableOrderedSetTest.cs | 21 +++ .../Foundation/NSMutableSetTest.cs | 21 +++ .../Foundation/NSOrderedSetTest.cs | 45 ++++++ 11 files changed, 461 insertions(+), 149 deletions(-) diff --git a/src/Foundation/NSDictionary.cs b/src/Foundation/NSDictionary.cs index 1cb081fbd3fd..ce357674ede9 100644 --- a/src/Foundation/NSDictionary.cs +++ b/src/Foundation/NSDictionary.cs @@ -129,6 +129,37 @@ internal static NSArray PickOdd (object f, object [] args) return NSArray.FromObjects (ret); } + // Checks: + // * 'objects' and 'keys' for null + // * count isn't negative + // * count isn't higher than the number of elements in either array + // returns false if an empty dictionary can be returned + private protected static bool ValidateFromObjectsAndKeys (T[] objects, K[] keys, nint count) + { + ArgumentNullException.ThrowIfNull (objects); + ArgumentNullException.ThrowIfNull (keys); + + if (count < 0 || objects.Length < count || keys.Length < count) + throw new ArgumentException (nameof (count)); + + return count > 0; + } + + // Checks: + // * 'objects' and 'keys' for null + // * 'objects' and 'keys' have the same number of elements + // returns false if an empty dictionary can be returned + private protected static bool ValidateFromObjectsAndKeys (T[] objects, K[] keys) + { + ArgumentNullException.ThrowIfNull (objects); + ArgumentNullException.ThrowIfNull (keys); + + if (objects.Length != keys.Length) + throw new ArgumentException (nameof (objects) + " and " + nameof (keys) + " arrays have different sizes"); + + return objects.Length > 0; + } + /// /// Creates a dictionary from a set of values and keys. /// @@ -137,12 +168,8 @@ internal static NSArray PickOdd (object f, object [] args) /// A new containing the specified key-value pairs. public static NSDictionary FromObjectsAndKeys (NSObject? [] objects, NSObject [] keys) { - if (objects is null) - throw new ArgumentNullException (nameof (objects)); - if (keys is null) - throw new ArgumentNullException (nameof (keys)); - if (objects.Length != keys.Length) - throw new ArgumentException (nameof (objects) + " and " + nameof (keys) + " arrays have different sizes"); + if (!ValidateFromObjectsAndKeys (objects, keys)) + return new NSDictionary (); return FromObjectsAndKeys (objects, keys, keys.Length); } @@ -160,16 +187,10 @@ public static NSDictionary FromObjectsAndKeys (NSObject? [] objects, NSObject [] /// public static NSDictionary FromObjectsAndKeys (object [] objects, object [] keys) { - if (objects is null) - throw new ArgumentNullException (nameof (objects)); - if (keys is null) - throw new ArgumentNullException (nameof (keys)); - if (objects.Length != keys.Length) - throw new ArgumentException (nameof (objects) + " and " + nameof (keys) + " arrays have different sizes"); + if (!ValidateFromObjectsAndKeys (objects, keys)) + return new NSDictionary (); - using (var no = NSArray.FromObjects (objects)) - using (var nk = NSArray.FromObjects (keys)) - return FromObjectsAndKeysInternal (no, nk); + return FromObjectsAndKeys (objects, keys, objects.Length); } /// @@ -177,18 +198,12 @@ public static NSDictionary FromObjectsAndKeys (object [] objects, object [] keys /// /// Array of values for the dictionary. Null elements are stored as . /// Array of keys for the dictionary. - /// Number of items to use in the creation; the number must be less than or equal to the number of elements in the arrays. + /// Number of items to use in the creation; the number must be less than or equal to the number of elements in both arrays. /// A new containing the specified key-value pairs. public static NSDictionary FromObjectsAndKeys (NSObject? [] objects, NSObject [] keys, nint count) { - if (objects is null) - throw new ArgumentNullException (nameof (objects)); - if (keys is null) - throw new ArgumentNullException (nameof (keys)); - if (objects.Length != keys.Length) - throw new ArgumentException (nameof (objects) + " and " + nameof (keys) + " arrays have different sizes"); - if (count < 1 || objects.Length < count || keys.Length < count) - throw new ArgumentException ("count"); + if (!ValidateFromObjectsAndKeys (objects, keys, count)) + return new NSDictionary (); using (var no = NSArray.FromNativeObjects (objects, count)) using (var nk = NSArray.FromNativeObjects (keys, count)) @@ -200,7 +215,7 @@ public static NSDictionary FromObjectsAndKeys (NSObject? [] objects, NSObject [] /// /// Array of values for the dictionary. /// Array of keys for the dictionary. - /// Number of items to use in the creation; the number must be less than or equal to the number of elements in the arrays. + /// Number of items to use in the creation; the number must be less than or equal to the number of elements in both arrays. /// A new containing the specified key-value pairs. /// /// @@ -209,14 +224,8 @@ public static NSDictionary FromObjectsAndKeys (NSObject? [] objects, NSObject [] /// public static NSDictionary FromObjectsAndKeys (object [] objects, object [] keys, nint count) { - if (objects is null) - throw new ArgumentNullException (nameof (objects)); - if (keys is null) - throw new ArgumentNullException (nameof (keys)); - if (objects.Length != keys.Length) - throw new ArgumentException (nameof (objects) + " and " + nameof (keys) + " arrays have different sizes"); - if (count < 1 || objects.Length < count || keys.Length < count) - throw new ArgumentException ("count"); + if (!ValidateFromObjectsAndKeys (objects, keys, count)) + return new NSDictionary (); using (var no = NSArray.FromObjects (count, objects)) using (var nk = NSArray.FromObjects (count, keys)) diff --git a/src/Foundation/NSDictionary_2.cs b/src/Foundation/NSDictionary_2.cs index 661d7760a002..93fdf49dd7d9 100644 --- a/src/Foundation/NSDictionary_2.cs +++ b/src/Foundation/NSDictionary_2.cs @@ -256,13 +256,8 @@ static NSDictionary GenericFromObjectsAndKeysInternal (NSArray obj /// A new dictionary containing the specified key-value pairs. public static NSDictionary FromObjectsAndKeys (TValue? [] objects, TKey [] keys, nint count) { - ArgumentNullException.ThrowIfNull (objects); - ArgumentNullException.ThrowIfNull (keys); - - if (objects.Length != keys.Length) - throw new ArgumentException (nameof (objects) + " and " + nameof (keys) + " arrays have different sizes"); - if (count < 1 || objects.Length < count) - throw new ArgumentException (nameof (count)); + if (!ValidateFromObjectsAndKeys (objects, keys, count)) + return new NSDictionary (); using (var no = NSArray.FromNativeObjects (objects, count)) using (var nk = NSArray.FromNativeObjects (keys, count)) @@ -277,11 +272,8 @@ public static NSDictionary FromObjectsAndKeys (TValue? [] objects, /// A new dictionary containing the specified key-value pairs. public static NSDictionary FromObjectsAndKeys (TValue? [] objects, TKey [] keys) { - ArgumentNullException.ThrowIfNull (objects); - ArgumentNullException.ThrowIfNull (keys); - - if (objects.Length != keys.Length) - throw new ArgumentException (nameof (objects) + " and " + nameof (keys) + " arrays have different sizes"); + if (!ValidateFromObjectsAndKeys (objects, keys)) + return new NSDictionary (); return FromObjectsAndKeys (objects, keys, keys.Length); } @@ -294,15 +286,10 @@ public static NSDictionary FromObjectsAndKeys (TValue? [] objects, /// A new dictionary containing the specified key-value pairs. public static NSDictionary FromObjectsAndKeys (object [] objects, object [] keys) { - ArgumentNullException.ThrowIfNull (objects); - ArgumentNullException.ThrowIfNull (keys); - - if (objects.Length != keys.Length) - throw new ArgumentException (nameof (objects) + " and " + nameof (keys) + " arrays have different sizes"); + if (!ValidateFromObjectsAndKeys (objects, keys)) + return new NSDictionary (); - using (var no = NSArray.FromObjects (objects)) - using (var nk = NSArray.FromObjects (keys)) - return GenericFromObjectsAndKeysInternal (no, nk); + return FromObjectsAndKeys (objects, keys, objects.Length); } /// @@ -314,13 +301,8 @@ public static NSDictionary FromObjectsAndKeys (object [] objects, /// A new dictionary containing the specified key-value pairs. public static NSDictionary FromObjectsAndKeys (NSObject? [] objects, NSObject [] keys, nint count) { - ArgumentNullException.ThrowIfNull (objects); - ArgumentNullException.ThrowIfNull (keys); - - if (objects.Length != keys.Length) - throw new ArgumentException (nameof (objects) + " and " + nameof (keys) + " arrays have different sizes"); - if (count < 1 || objects.Length < count || keys.Length < count) - throw new ArgumentException (nameof (count)); + if (!ValidateFromObjectsAndKeys (objects, keys, count)) + return new NSDictionary (); using (var no = NSArray.FromNativeObjects (objects, count)) using (var nk = NSArray.FromNativeObjects (keys, count)) @@ -336,13 +318,8 @@ public static NSDictionary FromObjectsAndKeys (NSObject? [] object /// A new dictionary containing the specified key-value pairs. public static NSDictionary FromObjectsAndKeys (object [] objects, object [] keys, nint count) { - ArgumentNullException.ThrowIfNull (objects); - ArgumentNullException.ThrowIfNull (keys); - - if (objects.Length != keys.Length) - throw new ArgumentException (nameof (objects) + " and " + nameof (keys) + " arrays have different sizes"); - if (count < 1 || objects.Length < count || keys.Length < count) - throw new ArgumentException (nameof (count)); + if (!ValidateFromObjectsAndKeys (objects, keys, count)) + return new NSDictionary (); using (var no = NSArray.FromObjects (count, objects)) using (var nk = NSArray.FromObjects (count, keys)) diff --git a/src/Foundation/NSMutableDictionary.cs b/src/Foundation/NSMutableDictionary.cs index 10a572a9896a..a6587a32f2ea 100644 --- a/src/Foundation/NSMutableDictionary.cs +++ b/src/Foundation/NSMutableDictionary.cs @@ -41,16 +41,10 @@ public partial class NSMutableDictionary : NSDictionary, IDictionary, IDictionar /// Thrown when the arrays have different sizes. public static NSMutableDictionary FromObjectsAndKeys (NSObject [] objects, NSObject [] keys) { - if (objects is null) - throw new ArgumentNullException (nameof (objects)); - if (keys is null) - throw new ArgumentNullException (nameof (keys)); - if (objects.Length != keys.Length) - throw new ArgumentException (nameof (objects) + " and " + nameof (keys) + " arrays have different sizes"); - - using (var no = NSArray.FromNSObjects (objects)) - using (var nk = NSArray.FromNSObjects (keys)) - return FromObjectsAndKeysInternal (no, nk); + if (!ValidateFromObjectsAndKeys (objects, keys)) + return new NSMutableDictionary (); + + return FromObjectsAndKeys (objects, keys, objects.Length); } /// Creates a mutable dictionary from the specified arrays of objects and keys. @@ -61,16 +55,10 @@ public static NSMutableDictionary FromObjectsAndKeys (NSObject [] objects, NSObj /// Thrown when the arrays have different sizes. public static NSMutableDictionary FromObjectsAndKeys (object [] objects, object [] keys) { - if (objects is null) - throw new ArgumentNullException (nameof (objects)); - if (keys is null) - throw new ArgumentNullException (nameof (keys)); - if (objects.Length != keys.Length) - throw new ArgumentException (nameof (objects) + " and " + nameof (keys) + " arrays have different sizes"); - - using (var no = NSArray.FromObjects (objects)) - using (var nk = NSArray.FromObjects (keys)) - return FromObjectsAndKeysInternal (no, nk); + if (!ValidateFromObjectsAndKeys (objects, keys)) + return new NSMutableDictionary (); + + return FromObjectsAndKeys (objects, keys, objects.Length); } /// Creates a mutable dictionary from the specified number of objects and keys from the arrays. @@ -79,20 +67,14 @@ public static NSMutableDictionary FromObjectsAndKeys (object [] objects, object /// The number of elements to copy from the arrays. /// A new mutable dictionary containing the specified objects and keys. /// Thrown when or is . - /// Thrown when the arrays have different sizes or is invalid. + /// Thrown when is invalid. public static NSMutableDictionary FromObjectsAndKeys (NSObject [] objects, NSObject [] keys, nint count) { - if (objects is null) - throw new ArgumentNullException (nameof (objects)); - if (keys is null) - throw new ArgumentNullException (nameof (keys)); - if (objects.Length != keys.Length) - throw new ArgumentException (nameof (objects) + " and " + nameof (keys) + " arrays have different sizes"); - if (count < 1 || objects.Length < count || keys.Length < count) - throw new ArgumentException (nameof (count)); - - using (var no = NSArray.FromNSObjects (objects)) - using (var nk = NSArray.FromNSObjects (keys)) + if (!ValidateFromObjectsAndKeys (objects, keys, count)) + return new NSMutableDictionary (); + + using (var no = NSArray.FromNativeObjects (objects, count)) + using (var nk = NSArray.FromNativeObjects (keys, count)) return FromObjectsAndKeysInternal (no, nk); } @@ -102,20 +84,14 @@ public static NSMutableDictionary FromObjectsAndKeys (NSObject [] objects, NSObj /// The number of elements to copy from the arrays. /// A new mutable dictionary containing the specified objects and keys. /// Thrown when or is . - /// Thrown when the arrays have different sizes or is invalid. + /// Thrown when is invalid. public static NSMutableDictionary FromObjectsAndKeys (object [] objects, object [] keys, nint count) { - if (objects is null) - throw new ArgumentNullException (nameof (objects)); - if (keys is null) - throw new ArgumentNullException (nameof (keys)); - if (objects.Length != keys.Length) - throw new ArgumentException (nameof (objects) + " and " + nameof (keys) + " arrays have different sizes"); - if (count < 1 || objects.Length < count || keys.Length < count) - throw new ArgumentException (nameof (count)); - - using (var no = NSArray.FromObjects (objects)) - using (var nk = NSArray.FromObjects (keys)) + if (!ValidateFromObjectsAndKeys (objects, keys, count)) + return new NSMutableDictionary (); + + using (var no = NSArray.FromObjects (count, objects)) + using (var nk = NSArray.FromObjects (count, keys)) return FromObjectsAndKeysInternal (no, nk); } diff --git a/src/Foundation/NSMutableDictionary_2.cs b/src/Foundation/NSMutableDictionary_2.cs index 590951e18f1c..473efef936af 100644 --- a/src/Foundation/NSMutableDictionary_2.cs +++ b/src/Foundation/NSMutableDictionary_2.cs @@ -265,15 +265,11 @@ public TValue? this [TKey index] { /// A new mutable dictionary containing the specified key-value pairs. public static NSMutableDictionary? FromObjectsAndKeys (TValue [] objects, TKey [] keys, nint count) { - ArgumentNullException.ThrowIfNull (objects); - ArgumentNullException.ThrowIfNull (keys); - if (objects.Length != keys.Length) - throw new ArgumentException (nameof (objects) + " and " + nameof (keys) + " arrays have different sizes"); - if (count < 1 || objects.Length < count) - throw new ArgumentException (nameof (count)); + if (!ValidateFromObjectsAndKeys (objects, keys, count)) + return new NSMutableDictionary (); - using (var no = NSArray.FromNSObjects (objects)) - using (var nk = NSArray.FromNSObjects (keys)) + using (var no = NSArray.FromNativeObjects (objects, count)) + using (var nk = NSArray.FromNativeObjects (keys, count)) return GenericFromObjectsAndKeysInternal (no, nk); } @@ -285,14 +281,10 @@ public TValue? this [TKey index] { /// A new mutable dictionary containing the specified key-value pairs. public static NSMutableDictionary? FromObjectsAndKeys (TValue [] objects, TKey [] keys) { - ArgumentNullException.ThrowIfNull (objects); - ArgumentNullException.ThrowIfNull (keys); - if (objects.Length != keys.Length) - throw new ArgumentException (nameof (objects) + " and " + nameof (keys) + " arrays have different sizes"); + if (!ValidateFromObjectsAndKeys (objects, keys)) + return new NSMutableDictionary (); - using (var no = NSArray.FromNSObjects (objects)) - using (var nk = NSArray.FromNSObjects (keys)) - return GenericFromObjectsAndKeysInternal (no, nk); + return FromObjectsAndKeys (objects, keys, objects.Length); } /// @@ -303,14 +295,10 @@ public TValue? this [TKey index] { /// A new mutable dictionary containing the specified key-value pairs. public static NSMutableDictionary? FromObjectsAndKeys (object [] objects, object [] keys) { - ArgumentNullException.ThrowIfNull (objects); - ArgumentNullException.ThrowIfNull (keys); - if (objects.Length != keys.Length) - throw new ArgumentException (nameof (objects) + " and " + nameof (keys) + " arrays have different sizes"); + if (!ValidateFromObjectsAndKeys (objects, keys)) + return new NSMutableDictionary (); - using (var no = NSArray.FromObjects (objects)) - using (var nk = NSArray.FromObjects (keys)) - return GenericFromObjectsAndKeysInternal (no, nk); + return FromObjectsAndKeys (objects, keys, objects.Length); } /// @@ -322,15 +310,11 @@ public TValue? this [TKey index] { /// A new mutable dictionary containing the specified key-value pairs. public static NSMutableDictionary? FromObjectsAndKeys (NSObject [] objects, NSObject [] keys, nint count) { - ArgumentNullException.ThrowIfNull (objects); - ArgumentNullException.ThrowIfNull (keys); - if (objects.Length != keys.Length) - throw new ArgumentException (nameof (objects) + " and " + nameof (keys) + " arrays have different sizes"); - if (count < 1 || objects.Length < count || keys.Length < count) - throw new ArgumentException (nameof (count)); + if (!ValidateFromObjectsAndKeys (objects, keys, count)) + return new NSMutableDictionary (); - using (var no = NSArray.FromNSObjects (objects)) - using (var nk = NSArray.FromNSObjects (keys)) + using (var no = NSArray.FromNativeObjects (objects, count)) + using (var nk = NSArray.FromNativeObjects (keys, count)) return GenericFromObjectsAndKeysInternal (no, nk); } @@ -343,15 +327,11 @@ public TValue? this [TKey index] { /// A new mutable dictionary containing the specified key-value pairs. public static NSMutableDictionary? FromObjectsAndKeys (object [] objects, object [] keys, nint count) { - ArgumentNullException.ThrowIfNull (objects); - ArgumentNullException.ThrowIfNull (keys); - if (objects.Length != keys.Length) - throw new ArgumentException (nameof (objects) + " and " + nameof (keys) + " arrays have different sizes"); - if (count < 1 || objects.Length < count || keys.Length < count) - throw new ArgumentException (nameof (count)); + if (!ValidateFromObjectsAndKeys (objects, keys, count)) + return new NSMutableDictionary (); - using (var no = NSArray.FromObjects (objects)) - using (var nk = NSArray.FromObjects (keys)) + using (var no = NSArray.FromObjects (count, objects)) + using (var nk = NSArray.FromObjects (count, keys)) return GenericFromObjectsAndKeysInternal (no, nk); } diff --git a/tests/monotouch-test/Foundation/NSDictionary2Test.cs b/tests/monotouch-test/Foundation/NSDictionary2Test.cs index ee55050bc907..e4c00e3fdea6 100644 --- a/tests/monotouch-test/Foundation/NSDictionary2Test.cs +++ b/tests/monotouch-test/Foundation/NSDictionary2Test.cs @@ -40,6 +40,17 @@ public void Ctor_Arrays () Assert.AreEqual ((string) (j [(NSString) "second-k"]), "second", "lookup2"); } + [Test] + public void Ctor_WithNullValue () + { + var key = (NSString) "key"; + var dict = new NSDictionary (key, null); + Assert.AreEqual ((nuint) 1, dict.Count, "count"); + var baseDict = (NSDictionary) dict; + var rawValue = baseDict.ObjectForKey (key); + Assert.IsInstanceOf (rawValue, "Null value should be NSNull"); + } + [Test] public void Ctor_NSDictionary () { diff --git a/tests/monotouch-test/Foundation/NSDictionaryTest.cs b/tests/monotouch-test/Foundation/NSDictionaryTest.cs index 57764bc937aa..cd738779b9b9 100644 --- a/tests/monotouch-test/Foundation/NSDictionaryTest.cs +++ b/tests/monotouch-test/Foundation/NSDictionaryTest.cs @@ -178,6 +178,26 @@ public void FromObjectsAndKeysTest_NullValue_NoCount () Assert.AreEqual (4, ((NSNumber) ns [new NSNumber (3)]).Int32Value, "Value 3"); } + [Test] + public void DictionaryCtorKeyValues_WithNull () + { + var key1 = new NSString ("key1"); + var key2 = new NSString ("key2"); + var value = new NSString ("value"); + + // Test null value + var dict = new NSDictionary (key1, null); + Assert.AreEqual ((nuint) 1, dict.Count, "count with null value"); + var rawValue = dict.ObjectForKey (key1); + Assert.IsInstanceOf (rawValue, "Null value should be NSNull"); + + // Test null in variadic args (value position) + dict = new NSDictionary (key1, value, key2, null); + Assert.AreEqual ((nuint) 2, dict.Count, "count with null in args"); + rawValue = dict.ObjectForKey (key2); + Assert.IsInstanceOf (rawValue, "Null value in args should be NSNull"); + } + [Test] public void Copy () { diff --git a/tests/monotouch-test/Foundation/NSMutableDictionary2Test.cs b/tests/monotouch-test/Foundation/NSMutableDictionary2Test.cs index 945c1c4d4e4b..4e06a23e62de 100644 --- a/tests/monotouch-test/Foundation/NSMutableDictionary2Test.cs +++ b/tests/monotouch-test/Foundation/NSMutableDictionary2Test.cs @@ -61,6 +61,151 @@ public void FromObjectsAndKeysGenericTest () Assert.AreEqual (dict [keys [i]], values [i], $"key lookup, Iteration: {i}"); } + [Test] + public void Ctor_WithNullValue () + { + var key = (NSString) "key"; + using (var dict = new NSMutableDictionary (key, null)) { + Assert.AreEqual ((nuint) 1, dict.Count, "count"); + var baseDict = (NSDictionary) dict; + var rawValue = baseDict.ObjectForKey (key); + Assert.IsInstanceOf (rawValue, "Null value should be NSNull"); + } + } + + [Test] + public void FromObjectsAndKeys_Generic_WithNull () + { + var keys = new NSString [] { (NSString) "key1", (NSString) "key2" }; + var values = new NSString? [] { (NSString) "value1", null }; + + using (var dict = NSMutableDictionary.FromObjectsAndKeys (values, keys)) { + Assert.IsNotNull (dict, "Dictionary should not be null"); + Assert.AreEqual ((nuint) 2, dict!.Count, "Count"); + Assert.AreEqual ("value1", dict [keys [0]].ToString (), "First value"); + var baseDict = (NSDictionary) dict; + var rawValue = baseDict.ObjectForKey (keys [1]); + Assert.IsInstanceOf (rawValue, "Null value should be NSNull"); + } + } + + [Test] + public void FromObjectsAndKeys_Generic_WithCount_WithNull () + { + var keys = new NSString [] { (NSString) "key1", (NSString) "key2", (NSString) "key3" }; + var values = new NSString? [] { (NSString) "value1", null, (NSString) "value3" }; + + using (var dict = NSMutableDictionary.FromObjectsAndKeys (values, keys, 2)) { + Assert.IsNotNull (dict, "Dictionary should not be null"); + Assert.AreEqual ((nuint) 2, dict!.Count, "Count"); + Assert.AreEqual ("value1", dict [keys [0]].ToString (), "First value"); + var baseDict = (NSDictionary) dict; + var rawValue = baseDict.ObjectForKey (keys [1]); + Assert.IsInstanceOf (rawValue, "Null value should be NSNull"); + } + } + + [Test] + public void FromObjectsAndKeys_Object_WithCount () + { + var keys = new object [] { "key1", "key2", "key3" }; + var objs = new object [] { "value1", "value2", "value3" }; + + using (var dict = NSMutableDictionary.FromObjectsAndKeys (objs, keys, 2)) { + Assert.IsNotNull (dict, "Dictionary should not be null"); + Assert.AreEqual ((nuint) 2, dict!.Count, "Count"); + Assert.AreEqual ("value1", dict [(NSString) "key1"].ToString (), "First value"); + Assert.AreEqual ("value2", dict [(NSString) "key2"].ToString (), "Second value"); + } + } + + [Test] + public void FromObjectsAndKeys_NSObject_WithCount_WithNull () + { + var keys = new NSObject [] { new NSString ("key1"), new NSString ("key2"), new NSString ("key3") }; + var objs = new NSObject? [] { new NSString ("value1"), null, new NSString ("value3") }; + + using (var dict = NSMutableDictionary.FromObjectsAndKeys (objs, keys, 2)) { + Assert.IsNotNull (dict, "Dictionary should not be null"); + Assert.AreEqual ((nuint) 2, dict!.Count, "Count"); + Assert.AreEqual ("value1", dict [(NSString) keys [0]].ToString (), "First value"); + var baseDict = (NSDictionary) dict; + var rawValue = baseDict.ObjectForKey (keys [1]); + Assert.IsInstanceOf (rawValue, "Null value should be NSNull"); + } + } + + [Test] + public void FromObjectsAndKeys_NSObject_WithCount () + { + var keys = new NSObject [] { new NSString ("key1"), new NSString ("key2"), new NSString ("key3") }; + var objs = new NSObject [] { new NSString ("value1"), new NSString ("value2"), new NSString ("value3") }; + + using (var dict = NSMutableDictionary.FromObjectsAndKeys (objs, keys, 2)) { + Assert.IsNotNull (dict, "Dictionary should not be null"); + Assert.AreEqual ((nuint) 2, dict!.Count, "Count"); + Assert.AreEqual ("value1", dict [(NSString) keys [0]].ToString (), "First value"); + Assert.AreEqual ("value2", dict [(NSString) keys [1]].ToString (), "Second value"); + } + } + + [Test] + public void FromObjectsAndKeys_Generic_WithCountZero () + { + var keys = new NSString [] { (NSString) "key1", (NSString) "key2" }; + var values = new NSString [] { (NSString) "value1", (NSString) "value2" }; + + using (var dict = NSMutableDictionary.FromObjectsAndKeys (values, keys, 0)) { + Assert.IsNotNull (dict, "Dictionary should not be null"); + Assert.AreEqual ((nuint) 0, dict!.Count, "Count should be 0"); + } + } + + [Test] + public void FromObjectsAndKeys_DifferentArrayLengths_WithCount () + { + var keys = new NSString [] { (NSString) "key1", (NSString) "key2" }; + var values = new NSString [] { (NSString) "value1", (NSString) "value2", (NSString) "value3", (NSString) "value4" }; + + // Should work fine since we only use first 2 items from each array + using (var dict = NSMutableDictionary.FromObjectsAndKeys (values, keys, 2)) { + Assert.IsNotNull (dict, "Dictionary should not be null"); + Assert.AreEqual ((nuint) 2, dict!.Count, "Count"); + Assert.AreEqual ("value1", dict [keys [0]].ToString (), "First value"); + Assert.AreEqual ("value2", dict [keys [1]].ToString (), "Second value"); + } + } + + [Test] + public void FromObjectsAndKeys_CountLargerThanKeys () + { + var keys = new NSString [] { (NSString) "key1", (NSString) "key2" }; + var values = new NSString [] { (NSString) "value1", (NSString) "value2", (NSString) "value3" }; + + // Should throw because count > keys.Length + Assert.Throws (() => NSMutableDictionary.FromObjectsAndKeys (values, keys, 3), "Should throw when count > keys.Length"); + } + + [Test] + public void FromObjectsAndKeys_CountLargerThanValues () + { + var keys = new NSString [] { (NSString) "key1", (NSString) "key2", (NSString) "key3" }; + var values = new NSString [] { (NSString) "value1", (NSString) "value2" }; + + // Should throw because count > values.Length + Assert.Throws (() => NSMutableDictionary.FromObjectsAndKeys (values, keys, 3), "Should throw when count > values.Length"); + } + + [Test] + public void FromObjectsAndKeys_NegativeCount () + { + var keys = new NSString [] { (NSString) "key1", (NSString) "key2" }; + var values = new NSString [] { (NSString) "value1", (NSString) "value2" }; + + // Should throw for negative count + Assert.Throws (() => NSMutableDictionary.FromObjectsAndKeys (values, keys, -1), "Should throw for negative count"); + } + [Test] public void KeyValue_Autorelease () { diff --git a/tests/monotouch-test/Foundation/NSMutableDictionaryTest.cs b/tests/monotouch-test/Foundation/NSMutableDictionaryTest.cs index af0c20d3d162..09b4e8a40047 100644 --- a/tests/monotouch-test/Foundation/NSMutableDictionaryTest.cs +++ b/tests/monotouch-test/Foundation/NSMutableDictionaryTest.cs @@ -204,5 +204,112 @@ public void MissingKey_IDictionaryContains () Assert.IsTrue (idict.Contains ((NSString) "existingKey"), "Contains should return true for existing key"); } } + + [Test] + public void FromObjectsAndKeys_WithNull () + { + var keys = new NSObject [] { new NSString ("key1"), new NSString ("key2") }; + var objs = new NSObject? [] { new NSString ("value1"), null }; + + using (var dict = NSMutableDictionary.FromObjectsAndKeys (objs, keys)) { + Assert.AreEqual ((nuint) 2, dict.Count, "Count"); + Assert.AreEqual ("value1", dict [keys [0]].ToString (), "First value"); + Assert.IsInstanceOf (dict [keys [1]], "Null value should be NSNull"); + } + } + + [Test] + public void FromObjectsAndKeys_NSObject_WithCount_WithNull () + { + var keys = new NSObject [] { new NSString ("key1"), new NSString ("key2"), new NSString ("key3") }; + var objs = new NSObject? [] { new NSString ("value1"), null, new NSString ("value3") }; + + using (var dict = NSMutableDictionary.FromObjectsAndKeys (objs, keys, 2)) { + Assert.AreEqual ((nuint) 2, dict.Count, "Count"); + Assert.AreEqual ("value1", dict [keys [0]].ToString (), "First value"); + Assert.IsInstanceOf (dict [keys [1]], "Null value should be NSNull"); + } + } + + [Test] + public void FromObjectsAndKeys_NSObject_WithCount () + { + var keys = new NSObject [] { new NSString ("key1"), new NSString ("key2"), new NSString ("key3") }; + var objs = new NSObject [] { new NSString ("value1"), new NSString ("value2"), new NSString ("value3") }; + + using (var dict = NSMutableDictionary.FromObjectsAndKeys (objs, keys, 2)) { + Assert.AreEqual ((nuint) 2, dict.Count, "Count"); + Assert.AreEqual ("value1", dict [keys [0]].ToString (), "First value"); + Assert.AreEqual ("value2", dict [keys [1]].ToString (), "Second value"); + } + } + + [Test] + public void FromObjectsAndKeys_NSObject_WithCountZero () + { + var keys = new NSObject [] { new NSString ("key1"), new NSString ("key2") }; + var objs = new NSObject [] { new NSString ("value1"), new NSString ("value2") }; + + using (var dict = NSMutableDictionary.FromObjectsAndKeys (objs, keys, 0)) { + Assert.AreEqual ((nuint) 0, dict.Count, "Count should be 0"); + } + } + + [Test] + public void FromObjectsAndKeys_Object_WithCount_WithNull () + { + var keys = new object [] { "key1", "key2", "key3" }; + var objs = new object [] { "value1", "value2", "value3" }; + + using (var dict = NSMutableDictionary.FromObjectsAndKeys (objs, keys, 2)) { + Assert.AreEqual ((nuint) 2, dict.Count, "Count"); + Assert.AreEqual ("value1", dict [(NSString) "key1"].ToString (), "First value"); + Assert.AreEqual ("value2", dict [(NSString) "key2"].ToString (), "Second value"); + } + } + + [Test] + public void FromObjectsAndKeys_DifferentArrayLengths_WithCount () + { + var keys = new NSObject [] { new NSString ("key1"), new NSString ("key2") }; + var objs = new NSObject [] { new NSString ("value1"), new NSString ("value2"), new NSString ("value3"), new NSString ("value4") }; + + // Should work fine since we only use first 2 items from each array + using (var dict = NSMutableDictionary.FromObjectsAndKeys (objs, keys, 2)) { + Assert.AreEqual ((nuint) 2, dict.Count, "Count"); + Assert.AreEqual ("value1", dict [keys [0]].ToString (), "First value"); + Assert.AreEqual ("value2", dict [keys [1]].ToString (), "Second value"); + } + } + + [Test] + public void FromObjectsAndKeys_CountLargerThanKeys () + { + var keys = new NSObject [] { new NSString ("key1"), new NSString ("key2") }; + var objs = new NSObject [] { new NSString ("value1"), new NSString ("value2"), new NSString ("value3") }; + + // Should throw because count > keys.Length + Assert.Throws (() => NSMutableDictionary.FromObjectsAndKeys (objs, keys, 3), "Should throw when count > keys.Length"); + } + + [Test] + public void FromObjectsAndKeys_CountLargerThanObjects () + { + var keys = new NSObject [] { new NSString ("key1"), new NSString ("key2"), new NSString ("key3") }; + var objs = new NSObject [] { new NSString ("value1"), new NSString ("value2") }; + + // Should throw because count > objs.Length + Assert.Throws (() => NSMutableDictionary.FromObjectsAndKeys (objs, keys, 3), "Should throw when count > objs.Length"); + } + + [Test] + public void FromObjectsAndKeys_NegativeCount () + { + var keys = new NSObject [] { new NSString ("key1"), new NSString ("key2") }; + var objs = new NSObject [] { new NSString ("value1"), new NSString ("value2") }; + + // Should throw for negative count + Assert.Throws (() => NSMutableDictionary.FromObjectsAndKeys (objs, keys, -1), "Should throw for negative count"); + } } } diff --git a/tests/monotouch-test/Foundation/NSMutableOrderedSetTest.cs b/tests/monotouch-test/Foundation/NSMutableOrderedSetTest.cs index bc792331bb1e..50b4bc5a4f1f 100644 --- a/tests/monotouch-test/Foundation/NSMutableOrderedSetTest.cs +++ b/tests/monotouch-test/Foundation/NSMutableOrderedSetTest.cs @@ -93,5 +93,26 @@ public void OperatorDifferentTest () Assert.IsFalse (oSet.Equals (oSet2), "NSMutableOrderedSetTest Equals must be false"); } } + + [Test] + public void Ctor_WithNull () + { + var str1 = (NSString) "1"; + NSObject? nullObj = null; + using (var set = new NSMutableOrderedSet (str1, nullObj)) { + Assert.AreEqual (2, (int) set.Count, "Count should include null"); + Assert.AreEqual (str1, set [0], "First item"); + Assert.IsInstanceOf (set [1], "Second item should be NSNull"); + } + } + + [Test] + public void Ctor_NullArray () + { + NSObject []? objs = null; + using (var set = new NSMutableOrderedSet (objs)) { + Assert.AreEqual (0, (int) set.Count, "Null array should create empty set"); + } + } } } diff --git a/tests/monotouch-test/Foundation/NSMutableSetTest.cs b/tests/monotouch-test/Foundation/NSMutableSetTest.cs index e3050be3138c..cadef860d89e 100644 --- a/tests/monotouch-test/Foundation/NSMutableSetTest.cs +++ b/tests/monotouch-test/Foundation/NSMutableSetTest.cs @@ -286,5 +286,26 @@ public void OperatorSubtract_SecondIsSupersetOfFirst () Assert.AreEqual ((nuint) 0, result.Count, "Superset Count"); } } + + [Test] + public void Ctor_WithNull () + { + var str1 = (NSString) "1"; + NSObject? nullObj = null; + using (var set = new NSMutableSet (str1, nullObj)) { + Assert.AreEqual ((nuint) 2, set.Count, "Count should include null"); + Assert.IsTrue (set.Contains (str1), "Should contain string"); + Assert.IsTrue (set.Contains (NSNull.Null), "Should contain NSNull"); + } + } + + [Test] + public void Ctor_NullArray () + { + NSObject []? objs = null; + using (var set = new NSMutableSet (objs)) { + Assert.AreEqual ((nuint) 0, set.Count, "Null array should create empty set"); + } + } } } diff --git a/tests/monotouch-test/Foundation/NSOrderedSetTest.cs b/tests/monotouch-test/Foundation/NSOrderedSetTest.cs index 430d845736c1..b4ef7934b167 100644 --- a/tests/monotouch-test/Foundation/NSOrderedSetTest.cs +++ b/tests/monotouch-test/Foundation/NSOrderedSetTest.cs @@ -167,5 +167,50 @@ public void OperatorDifferentTest () Assert.IsFalse (oSet.Equals (oSet2), "NSOrderedSetTest Equals must be false"); } } + + [Test] + public void Ctor_WithNull () + { + var str1 = (NSString) "1"; + NSObject? nullObj = null; + using (var set = new NSOrderedSet (str1, nullObj)) { + Assert.AreEqual (2, (int) set.Count, "Count should include null"); + Assert.AreEqual (str1, set [0], "First item"); + Assert.IsInstanceOf (set [1], "Second item should be NSNull"); + } + } + + [Test] + public void Ctor_NullArray () + { + NSObject []? objs = null; + using (var set = new NSOrderedSet (objs)) { + Assert.AreEqual (0, (int) set.Count, "Null array should create empty set"); + } + } + + [Test] + public void MakeNSOrderedSet_WithNull () + { + var str1 = (NSString) "1"; + var str2 = (NSString) "2"; + var values = new NSString? [] { str1, null, str2 }; + using (var set = NSOrderedSet.MakeNSOrderedSet (values)) { + Assert.AreEqual (3, (int) set.Count, "Count should include null"); + Assert.AreEqual (str1, set [0], "First item"); + Assert.IsInstanceOf (set [1], "Second item should be NSNull"); + Assert.AreEqual (str2, set [2], "Third item"); + } + } + + [Test] + public void MakeNSOrderedSet_NullArray () + { + NSString []? values = null; + using (var set = NSOrderedSet.MakeNSOrderedSet (values)) { + Assert.IsNotNull (set, "Should create a set"); + Assert.AreEqual (0, (int) set.Count, "Null array should create empty set"); + } + } } } From 79d4963f4e23b00ec5cc63fffef378eabe33f935 Mon Sep 17 00:00:00 2001 From: GitHub Actions Autoformatter Date: Fri, 23 Jan 2026 12:02:43 +0000 Subject: [PATCH 2/2] Auto-format source code --- src/Foundation/NSDictionary.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Foundation/NSDictionary.cs b/src/Foundation/NSDictionary.cs index ce357674ede9..16a2cc1b4f12 100644 --- a/src/Foundation/NSDictionary.cs +++ b/src/Foundation/NSDictionary.cs @@ -134,7 +134,7 @@ internal static NSArray PickOdd (object f, object [] args) // * count isn't negative // * count isn't higher than the number of elements in either array // returns false if an empty dictionary can be returned - private protected static bool ValidateFromObjectsAndKeys (T[] objects, K[] keys, nint count) + private protected static bool ValidateFromObjectsAndKeys (T [] objects, K [] keys, nint count) { ArgumentNullException.ThrowIfNull (objects); ArgumentNullException.ThrowIfNull (keys); @@ -149,7 +149,7 @@ private protected static bool ValidateFromObjectsAndKeys (T[] objects, K[] // * 'objects' and 'keys' for null // * 'objects' and 'keys' have the same number of elements // returns false if an empty dictionary can be returned - private protected static bool ValidateFromObjectsAndKeys (T[] objects, K[] keys) + private protected static bool ValidateFromObjectsAndKeys (T [] objects, K [] keys) { ArgumentNullException.ThrowIfNull (objects); ArgumentNullException.ThrowIfNull (keys);