PageRenderTime 47ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/Utilities/Collections/DictionaryBase.cs

#
C# | 1069 lines | 607 code | 95 blank | 367 comment | 30 complexity | 7d1528dd5795ed37e22d307d202e4f0f MD5 | raw file
Possible License(s): Apache-2.0
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Text;
  6. using NUnit.Framework;
  7. namespace Delta.Utilities.Collections
  8. {
  9. /// <summary>
  10. /// DictionaryBase is a base class for generics like the .NET 1.1 one.
  11. /// Implements IDictionary&lt;T&gt; and IDictionary.
  12. /// <para />
  13. /// This site contains examples and a great explanation of dictionaries:
  14. /// http://www.dotnetperls.com/dictionary-keys
  15. /// </summary>
  16. /// <typeparam name="TKey">Key type</typeparam>
  17. /// <typeparam name="TValue">Value type</typeparam>
  18. [DebuggerDisplay("{DebuggerToString()}")]
  19. public abstract class DictionaryBase<TKey, TValue>
  20. : CollectionBase<KeyValuePair<TKey, TValue>>,
  21. IDictionary<TKey, TValue>, IDictionary
  22. {
  23. #region KeysCollection Class
  24. /// <summary>
  25. /// A private class that implements ICollection&lt;TKey&gt; and ICollection
  26. /// for the Keys collection. The collection is read-only.
  27. /// </summary>
  28. /// <returns>Collection base</returns>
  29. private sealed class KeysCollection : CollectionBase<TKey>
  30. {
  31. #region Count (Public)
  32. /// <summary>
  33. /// Count
  34. /// </summary>
  35. /// <returns>Int</returns>
  36. public override int Count
  37. {
  38. get
  39. {
  40. return myDictionary.Count;
  41. } // get
  42. }
  43. #endregion
  44. #region Private
  45. #region myDictionary (Private)
  46. /// <summary>
  47. /// My dictionary
  48. /// </summary>
  49. private readonly DictionaryBase<TKey, TValue> myDictionary;
  50. #endregion
  51. #endregion
  52. #region Constructors
  53. /// <summary>
  54. /// Constructor.
  55. /// </summary>
  56. /// <param name="myDictionary">The dictionary this is associated with.
  57. /// </param>
  58. public KeysCollection(DictionaryBase<TKey, TValue> myDictionary)
  59. {
  60. this.myDictionary = myDictionary;
  61. }
  62. #endregion
  63. #region GetEnumerator (Public)
  64. /// <summary>
  65. /// Get enumerator
  66. /// </summary>
  67. /// <returns>IEnumerator</returns>
  68. public override IEnumerator<TKey> GetEnumerator()
  69. {
  70. foreach (KeyValuePair<TKey, TValue> pair in myDictionary)
  71. {
  72. yield return pair.Key;
  73. }
  74. }
  75. #endregion
  76. #region Contains (Public)
  77. /// <summary>
  78. /// Contains
  79. /// </summary>
  80. /// <param name="key">Key</param>
  81. /// <returns>True if the key was found, false otherwise</returns>
  82. public override bool Contains(TKey key)
  83. {
  84. return myDictionary.ContainsKey(key);
  85. }
  86. #endregion
  87. #region Clear (Public)
  88. /// <summary>
  89. /// Clear
  90. /// </summary>
  91. public override void Clear()
  92. {
  93. throw new Exception("It is not allowed to clear the keys!");
  94. }
  95. #endregion
  96. #region Remove (Public)
  97. /// <summary>
  98. /// Remove. Do not call, this method is not implemented!
  99. /// </summary>
  100. /// <param name="item">Item</param>
  101. /// <returns>True if the item was removed.</returns>
  102. public override bool Remove(TKey item)
  103. {
  104. throw new Exception("It is not allowed to remove an item.");
  105. }
  106. #endregion
  107. }
  108. #endregion
  109. #region ValuesCollection Class
  110. /// <summary>
  111. /// A private class that implements ICollection&lt;TValue&gt; and
  112. /// ICollection for the Values collection. The collection is read-only.
  113. /// </summary>
  114. /// <returns>Collection base</returns>
  115. private sealed class ValuesCollection : CollectionBase<TValue>
  116. {
  117. #region Count (Public)
  118. /// <summary>
  119. /// Count
  120. /// </summary>
  121. /// <returns>Int</returns>
  122. public override int Count
  123. {
  124. get
  125. {
  126. return myDictionary.Count;
  127. } // get
  128. }
  129. #endregion
  130. #region Private
  131. #region myDictionary (Private)
  132. /// <summary>
  133. /// My dictionary
  134. /// </summary>
  135. private readonly DictionaryBase<TKey, TValue> myDictionary;
  136. #endregion
  137. #endregion
  138. #region Constructors
  139. /// <summary>
  140. /// Create values collection
  141. /// </summary>
  142. /// <param name="myDictionary">My dictionary</param>
  143. public ValuesCollection(DictionaryBase<TKey, TValue> myDictionary)
  144. {
  145. this.myDictionary = myDictionary;
  146. }
  147. #endregion
  148. #region GetEnumerator (Public)
  149. /// <summary>
  150. /// Get enumerator
  151. /// </summary>
  152. /// <returns>IEnumerator</returns>
  153. public override IEnumerator<TValue> GetEnumerator()
  154. {
  155. foreach (KeyValuePair<TKey, TValue> pair in myDictionary)
  156. {
  157. yield return pair.Value;
  158. }
  159. }
  160. #endregion
  161. #region Clear (Public)
  162. /// <summary>
  163. /// Clear
  164. /// </summary>
  165. public override void Clear()
  166. {
  167. throw new Exception("It is not allowed to clear the values!");
  168. }
  169. #endregion
  170. #region Remove (Public)
  171. /// <summary>
  172. /// Remove. Do not call, this method is not implemented!
  173. /// </summary>
  174. /// <param name="item">Item</param>
  175. /// <returns>True if removing the item succeeded.</returns>
  176. public override bool Remove(TValue item)
  177. {
  178. throw new Exception("It is not allowed to remove an item.");
  179. }
  180. #endregion
  181. }
  182. #endregion
  183. #region DictionaryEnumeratorWrapper Class
  184. /// <summary>
  185. /// A class that wraps a IDictionaryEnumerator around an IEnumerator that
  186. /// enumerates KeyValuePairs. This is useful in implementing IDictionary,
  187. /// because IEnumerator can be implemented with an iterator, but
  188. /// IDictionaryEnumerator cannot.
  189. /// </summary>
  190. /// <returns>IDictionary enumerator</returns>
  191. private class DictionaryEnumeratorWrapper : IDictionaryEnumerator
  192. {
  193. #region Entry (Public)
  194. /// <summary>
  195. /// Entry
  196. /// </summary>
  197. /// <returns>Dictionary entry</returns>
  198. public DictionaryEntry Entry
  199. {
  200. get
  201. {
  202. KeyValuePair<TKey, TValue> pair = enumerator.Current;
  203. DictionaryEntry entry = new DictionaryEntry();
  204. if (pair.Key != null)
  205. {
  206. entry.Key = pair.Key;
  207. }
  208. entry.Value = pair.Value;
  209. return entry;
  210. }
  211. }
  212. #endregion
  213. #region Key (Public)
  214. /// <summary>
  215. /// Key
  216. /// </summary>
  217. /// <returns>Object</returns>
  218. public object Key
  219. {
  220. get
  221. {
  222. KeyValuePair<TKey, TValue> pair = enumerator.Current;
  223. return pair.Key;
  224. }
  225. }
  226. #endregion
  227. #region Value (Public)
  228. /// <summary>
  229. /// Value
  230. /// </summary>
  231. /// <returns>Object</returns>
  232. public object Value
  233. {
  234. get
  235. {
  236. KeyValuePair<TKey, TValue> pair = enumerator.Current;
  237. return pair.Value;
  238. }
  239. }
  240. #endregion
  241. #region Current (Public)
  242. /// <summary>
  243. /// Current
  244. /// </summary>
  245. /// <returns>Object</returns>
  246. public object Current
  247. {
  248. get
  249. {
  250. return Entry;
  251. }
  252. }
  253. #endregion
  254. #region Private
  255. #region enumerator (Private)
  256. /// <summary>
  257. /// Enumerator
  258. /// </summary>
  259. private readonly IEnumerator<KeyValuePair<TKey, TValue>> enumerator;
  260. #endregion
  261. #endregion
  262. #region Constructors
  263. /// <summary>
  264. /// Constructor.
  265. /// </summary>
  266. /// <param name="enumerator">
  267. /// The enumerator of KeyValuePairs that is being wrapped.
  268. /// </param>
  269. public DictionaryEnumeratorWrapper(
  270. IEnumerator<KeyValuePair<TKey, TValue>> enumerator)
  271. {
  272. this.enumerator = enumerator;
  273. }
  274. #endregion
  275. #region IEnumerator Members
  276. /// <summary>
  277. /// Move next
  278. /// </summary>
  279. /// <returns>True if there is a next item for the enumerator.</returns>
  280. public bool MoveNext()
  281. {
  282. return enumerator.MoveNext();
  283. }
  284. /// <summary>
  285. /// Reset. Do not call, this method is not implemented!
  286. /// </summary>
  287. public void Reset()
  288. {
  289. throw new NotSupportedException(
  290. "Reset is not supported on this enumerator.");
  291. }
  292. #endregion
  293. }
  294. #endregion
  295. #region Item (Public)
  296. /// <summary>
  297. /// The indexer of the dictionary. This is used to store keys and values
  298. /// and retrieve values from the dictionary. The setter accessor must be
  299. /// overridden in the derived class.
  300. /// </summary>
  301. /// <param name="key">Key to find in the dictionary.</param>
  302. /// <returns>The value associated with the key.</returns>
  303. /// <exception cref="KeyNotFoundException">Thrown from the get accessor
  304. /// if the key was not found in the dictionary.</exception>
  305. public virtual TValue this[TKey key]
  306. {
  307. get
  308. {
  309. TValue value;
  310. if (TryGetValue(key, out value))
  311. {
  312. return value;
  313. }
  314. else
  315. {
  316. throw new KeyNotFoundException(
  317. "The key was not found in the collection.");
  318. }
  319. }
  320. set
  321. {
  322. throw new NotSupportedException(
  323. "The set accessor of the indexer must be overridden.");
  324. }
  325. }
  326. #endregion
  327. #region Keys (Public)
  328. /// <summary>
  329. /// Returns a collection of the keys in this dictionary.
  330. /// </summary>
  331. /// <value>A read-only collection of the keys in this dictionary.</value>
  332. public virtual ICollection<TKey> Keys
  333. {
  334. get
  335. {
  336. return new KeysCollection(this);
  337. } // get
  338. }
  339. #endregion
  340. #region Values (Public)
  341. /// <summary>
  342. /// Returns a collection of the values in this dictionary. The ordering of
  343. /// values in this collection is the same as that in the Keys collection.
  344. /// </summary>
  345. /// <value>A read-only collection of the values in this dictionary.</value>
  346. public virtual ICollection<TValue> Values
  347. {
  348. get
  349. {
  350. return new ValuesCollection(this);
  351. } // get
  352. }
  353. #endregion
  354. #region Private
  355. #region IsFixedSize (Private)
  356. /// <summary>
  357. /// Returns whether this dictionary is fixed size.
  358. /// This implemented always returns false.
  359. /// </summary>
  360. /// <typeparam name="TKey">TKey</typeparam>
  361. /// <typeparam name="TValue">TValue</typeparam>
  362. /// <value>Always returns false.</value>
  363. bool IDictionary.IsFixedSize
  364. {
  365. get
  366. {
  367. return false;
  368. } // get
  369. }
  370. #endregion
  371. #region IsReadOnly (Private)
  372. /// <summary>
  373. /// Returns if this dictionary is read-only.
  374. /// This implementation always returns false.
  375. /// </summary>
  376. /// <typeparam name="TKey">TKey</typeparam>
  377. /// <typeparam name="TValue">TValue</typeparam>
  378. /// <value>Always returns false.</value>
  379. bool IDictionary.IsReadOnly
  380. {
  381. get
  382. {
  383. return false;
  384. } // get
  385. }
  386. #endregion
  387. #region Keys (Private)
  388. /// <summary>
  389. /// Returns a collection of all the keys in the dictionary. The values in
  390. /// this collection will be enumerated in the same order as the
  391. /// (overridden) GetEnumerator method.
  392. /// </summary>
  393. /// <typeparam name="TKey">TKey</typeparam>
  394. /// <typeparam name="TValue">TValue</typeparam>
  395. /// <value>The collection of keys.</value>
  396. ICollection IDictionary.Keys
  397. {
  398. get
  399. {
  400. return new KeysCollection(this);
  401. } // get
  402. }
  403. #endregion
  404. #region Values (Private)
  405. /// <summary>
  406. /// Returns a collection of all the values in the dictionary. The values
  407. /// in this collection will be enumerated in the same order as the
  408. /// (overridden) GetEnumerator method.
  409. /// </summary>
  410. /// <typeparam name="TKey">TKey</typeparam>
  411. /// <typeparam name="TValue">TValue</typeparam>
  412. /// <value>The collection of values.</value>
  413. ICollection IDictionary.Values
  414. {
  415. get
  416. {
  417. return new ValuesCollection(this);
  418. } // get
  419. }
  420. #endregion
  421. #region Item (Private)
  422. /// <summary>
  423. /// Gets or sets the value associated with a given key. When getting a
  424. /// value, if this key is not found in the collection, then null is
  425. /// returned. When setting a value, the value replaces any existing value
  426. /// in the dictionary. If either the key or value are not of the correct
  427. /// type for this dictionary, an ArgumentException is thrown.
  428. /// </summary>
  429. /// <value>The value associated with the key, or null if the key was not
  430. /// present.</value>
  431. /// <exception cref="ArgumentException">Key could not be converted to TKey,
  432. /// or value could not be converted to TValue.</exception>
  433. object IDictionary.this[object key]
  434. {
  435. get
  436. {
  437. if (key is TKey ||
  438. key == null)
  439. {
  440. TKey theKey = (TKey)key;
  441. TValue theValue;
  442. // The IDictionary (non-generic) indexer returns null for not found,
  443. // instead of throwing an exception like the generic IDictionary
  444. // indexer.
  445. if (TryGetValue(theKey, out theValue))
  446. {
  447. return theValue;
  448. }
  449. else
  450. {
  451. return null;
  452. }
  453. }
  454. else
  455. {
  456. return null;
  457. }
  458. }
  459. set
  460. {
  461. CheckGenericType<TKey>("key", key);
  462. CheckGenericType<TValue>("value", value);
  463. this[(TKey)key] = (TValue)value;
  464. }
  465. }
  466. #endregion
  467. #endregion
  468. #region ICollection<KeyValuePair<TKey,TValue>> Members
  469. /// <summary>
  470. /// Adds a key-value pair to the collection. This implementation calls the
  471. /// Add method with the Key and Value from the item.
  472. /// </summary>
  473. /// <param name="item">
  474. /// A KeyValuePair contains the Key and Value to add.
  475. /// </param>
  476. public override void Add(KeyValuePair<TKey, TValue> item)
  477. {
  478. Add(item.Key, item.Value);
  479. }
  480. /// <summary>
  481. /// Clears the dictionary.
  482. /// This method must be overridden in the derived class.
  483. /// </summary>
  484. public abstract override void Clear();
  485. /// <summary>
  486. /// Determines if a dictionary contains a given KeyValuePair. This
  487. /// implementation checks to see if the dictionary contains the given key,
  488. /// and if the value associated with the key is equal to (via
  489. /// object.Equals) the value.
  490. /// </summary>
  491. /// <param name="item">
  492. /// A KeyValuePair containing the Key and Value to check for.
  493. /// </param>
  494. /// <returns>True if the item was found, false otherwise.</returns>
  495. public override bool Contains(KeyValuePair<TKey, TValue> item)
  496. {
  497. if (ContainsKey(item.Key))
  498. {
  499. return (Equals(this[item.Key], item.Value));
  500. }
  501. else
  502. {
  503. return false;
  504. }
  505. }
  506. /// <summary>
  507. /// Determines if a dictionary contains a given KeyValuePair, and if so,
  508. /// removes it. This implementation checks to see if the dictionary
  509. /// contains the given key, and if the value associated with the key is
  510. /// equal to (via object.Equals) the value. If so, the key-value pair is
  511. /// removed.
  512. /// </summary>
  513. /// <param name="item">
  514. /// A KeyValuePair containing the Key and Value to check for.
  515. /// </param>
  516. /// <returns>
  517. /// True if the item was found and removed. False otherwise.
  518. /// </returns>
  519. public override bool Remove(KeyValuePair<TKey, TValue> item)
  520. {
  521. if (((ICollection<KeyValuePair<TKey, TValue>>)this).Contains(item))
  522. {
  523. return Remove(item.Key);
  524. }
  525. else
  526. {
  527. return false;
  528. }
  529. }
  530. #endregion
  531. #region IDictionary Members
  532. /// <summary>
  533. /// Adds a key-value pair to the collection. If key or value are not of
  534. /// the expected types, an ArgumentException is thrown. If both key and
  535. /// value are of the expected types, the (overridden) Add method is called
  536. /// with the key and value to add.
  537. /// </summary>
  538. /// <param name="key">Key to add</param>
  539. /// <param name="value">Value to add</param>
  540. /// <exception cref="ArgumentException">Key or value are not of the
  541. /// expected type for this dictionary.</exception>
  542. void IDictionary.Add(object key, object value)
  543. {
  544. CheckGenericType<TKey>("key", key);
  545. CheckGenericType<TValue>("value", value);
  546. Add((TKey)key, (TValue)value);
  547. }
  548. /// <summary>
  549. /// Clears this dictionary. Calls the (overridden) Clear method.
  550. /// </summary>
  551. void IDictionary.Clear()
  552. {
  553. Clear();
  554. }
  555. /// <summary>
  556. /// Determines if this dictionary contains a key equal to
  557. /// <paramref name="key"/>. The dictionary is not changed. Calls the
  558. /// (overridden) ContainsKey method. If key is not of the correct TKey for
  559. /// the dictionary, false is returned.
  560. /// </summary>
  561. /// <param name="key">The key to search for.</param>
  562. /// <returns>True if the dictionary contains key. False if the dictionary
  563. /// does not contain key.</returns>
  564. bool IDictionary.Contains(object key)
  565. {
  566. if (key is TKey ||
  567. key == null)
  568. {
  569. return ContainsKey((TKey)key);
  570. }
  571. else
  572. {
  573. return false;
  574. }
  575. }
  576. /// <summary>
  577. /// Returns an enumerator that enumerates all entries in the dictionary.
  578. /// Each entry is returned as a DictionaryEntry. The entries are enumerated
  579. /// in the same orders as the (overridden) GetEnumerator method.
  580. /// </summary>
  581. /// <returns>An enumerator for enumerating all the elements in the
  582. /// OrderedDictionary.</returns>
  583. IDictionaryEnumerator IDictionary.GetEnumerator()
  584. {
  585. // You can't implement this directly with an iterator, because iterators
  586. // automatically implement IEnumerator, not IDictionaryEnumerator.
  587. // We use the helper class DictionaryEnumeratorWrapper.
  588. return new DictionaryEnumeratorWrapper(GetEnumerator());
  589. }
  590. /// <summary>
  591. /// Removes the key (and associated value) from the collection that is
  592. /// equal to the passed in key. If no key in the dictionary is equal to
  593. /// the passed key, the dictionary is unchanged. Calls the (overridden)
  594. /// Remove method. If key is not of the correct TKey for the dictionary,
  595. /// the dictionary is unchanged.
  596. /// </summary>
  597. /// <param name="key">The key to remove.</param>
  598. /// <exception cref="ArgumentException">key could not be converted to TKey.
  599. /// </exception>
  600. void IDictionary.Remove(object key)
  601. {
  602. if (key is TKey ||
  603. key == null)
  604. {
  605. Remove((TKey)key);
  606. }
  607. }
  608. #endregion
  609. #region IDictionary<TKey,TValue> Members
  610. /// <summary>
  611. /// Adds a new key-value pair to the dictionary.
  612. /// </summary>
  613. /// <remarks>The default implementation of this method
  614. /// checks to see if the key already exists using
  615. /// ContainsKey, then calls the indexer setter if the key doesn't
  616. /// already exist. </remarks>
  617. /// <param name="key">Key to add.</param>
  618. /// <param name="value">Value to associated with the key.</param>
  619. /// <exception cref="ArgumentException">key is already present in the
  620. /// dictionary</exception>
  621. public virtual void Add(TKey key, TValue value)
  622. {
  623. if (ContainsKey(key))
  624. {
  625. throw new ArgumentException(
  626. "The key was already present in the dictionary.", "key");
  627. }
  628. else
  629. {
  630. this[key] = value;
  631. }
  632. }
  633. /// <summary>
  634. /// Determines whether a given key is found in the dictionary.
  635. /// </summary>
  636. /// <remarks>The default implementation simply calls TryGetValue and
  637. /// returns what it returns.</remarks>
  638. /// <param name="key">Key to look for in the dictionary.</param>
  639. /// <returns>True if the key is present in the dictionary.</returns>
  640. public virtual bool ContainsKey(TKey key)
  641. {
  642. TValue dummy;
  643. return TryGetValue(key, out dummy);
  644. }
  645. /// <summary>
  646. /// Removes a key from the dictionary.
  647. /// This method must be overridden in the derived class.
  648. /// </summary>
  649. /// <param name="key">Key to remove from the dictionary.</param>
  650. /// <returns>True if the key was found, false otherwise.</returns>
  651. public abstract bool Remove(TKey key);
  652. /// <summary>
  653. /// Determines if this dictionary contains a key equal to
  654. /// <paramref name="key"/>. If so, the value associated with that key is
  655. /// returned through the value parameter. This method must be overridden
  656. /// by the derived class.
  657. /// </summary>
  658. /// <param name="key">The key to search for.</param>
  659. /// <param name="value">Returns the value associated with key, if true was
  660. /// returned.</param>
  661. /// <returns>True if the dictionary contains key. False if the dictionary
  662. /// does not contain key.</returns>
  663. public abstract bool TryGetValue(TKey key, out TValue value);
  664. #endregion
  665. #region IEnumerable Members
  666. /// <summary>
  667. /// Returns an enumerator that enumerates all entries in the dictionary.
  668. /// Each entry is returned as a DictionaryEntry. The entries are enumerated
  669. /// in the same orders as the (overridden) GetEnumerator method.
  670. /// </summary>
  671. /// <returns>An enumerator for enumerating all the elements in the
  672. /// OrderedDictionary.</returns>
  673. IEnumerator IEnumerable.GetEnumerator()
  674. {
  675. return ((IDictionary)this).GetEnumerator();
  676. }
  677. #endregion
  678. #region ToString (Public)
  679. /// <summary>
  680. /// Shows the string representation of the dictionary. The string
  681. /// representation contains a list of the mappings in the dictionary.
  682. /// </summary>
  683. /// <returns>The string representation of the dictionary.</returns>
  684. public override string ToString()
  685. {
  686. bool firstItem = true;
  687. StringBuilder builder = new StringBuilder();
  688. builder.Append("{");
  689. // Call ToString on each item and put it in.
  690. foreach (KeyValuePair<TKey, TValue> pair in this)
  691. {
  692. if (!firstItem)
  693. {
  694. builder.Append(", ");
  695. }
  696. if (pair.Key == null)
  697. {
  698. builder.Append("null");
  699. }
  700. else
  701. {
  702. builder.Append(pair.Key.ToString());
  703. }
  704. builder.Append("->");
  705. if (pair.Value == null)
  706. {
  707. builder.Append("null");
  708. }
  709. else
  710. {
  711. builder.Append(pair.Value.ToString());
  712. }
  713. firstItem = false;
  714. }
  715. builder.Append("}");
  716. return builder.ToString();
  717. }
  718. #endregion
  719. #region Methods (Private)
  720. #region DebuggerToString
  721. /// <summary>
  722. /// Display the contents of the dictionary in the debugger. This is
  723. /// intentionally private, it is called only from the debugger due to the
  724. /// presence of the DebuggerDisplay attribute. It is similar format to
  725. /// ToString(), but is limited to 250-300 characters or so, so as not to
  726. /// overload the debugger.
  727. /// </summary>
  728. /// <returns>The string representation of the items in the collection,
  729. /// similar in format to ToString().</returns>
  730. internal new string DebuggerToString()
  731. {
  732. const int MAXLENGTH = 250;
  733. bool firstItem = true;
  734. StringBuilder builder = new StringBuilder();
  735. builder.Append("{");
  736. // Call ToString on each item and put it in.
  737. foreach (KeyValuePair<TKey, TValue> pair in this)
  738. {
  739. if (builder.Length >= MAXLENGTH)
  740. {
  741. builder.Append(", ...");
  742. break;
  743. }
  744. if (!firstItem)
  745. {
  746. builder.Append(", ");
  747. }
  748. if (pair.Key == null)
  749. {
  750. builder.Append("null");
  751. }
  752. else
  753. {
  754. builder.Append(pair.Key.ToString());
  755. }
  756. builder.Append("->");
  757. if (pair.Value == null)
  758. {
  759. builder.Append("null");
  760. }
  761. else
  762. {
  763. builder.Append(pair.Value.ToString());
  764. }
  765. firstItem = false;
  766. }
  767. builder.Append("}");
  768. return builder.ToString();
  769. }
  770. #endregion
  771. #region CheckGenericType
  772. /// <summary>
  773. /// Check that the given parameter is of the expected generic type.
  774. /// Throw an ArgumentException if it isn't.
  775. /// </summary>
  776. /// <typeparam name="ExpectedType">Expected type of the parameter
  777. /// </typeparam>
  778. /// <param name="name">parameter name</param>
  779. /// <param name="value">parameter value</param>
  780. private void CheckGenericType<ExpectedType>(string name, object value)
  781. {
  782. if (!(value is ExpectedType))
  783. {
  784. throw new ArgumentException(
  785. "The value " + value + " isn't of type " + typeof(ExpectedType) +
  786. " and can't be used in this generic collection.", name);
  787. }
  788. }
  789. #endregion
  790. #endregion
  791. }
  792. /// <summary>
  793. /// Test dictionary base helper, needs to be an extra class because
  794. /// DictionaryBase is a generic class.
  795. /// </summary>
  796. internal class DictionaryBaseTests
  797. {
  798. #region Helpers
  799. #region TestDictionary
  800. /// <summary>
  801. /// Test dictionary
  802. /// </summary>
  803. private class TestDictionary<TKey, TValue> : DictionaryBase<TKey, TValue>
  804. {
  805. #region Count (Public)
  806. /// <summary>
  807. /// Count
  808. /// </summary>
  809. /// <returns>Int</returns>
  810. public override int Count
  811. {
  812. get
  813. {
  814. return keys.Count;
  815. } // get
  816. }
  817. #endregion
  818. #region Item (Public)
  819. /// <summary>
  820. /// This
  821. /// </summary>
  822. /// <param name="key">Key</param>
  823. /// <returns>TValue</returns>
  824. public override TValue this[TKey key]
  825. {
  826. set
  827. {
  828. int index = keys.IndexOf(key);
  829. if (index < 0)
  830. {
  831. keys.Add(key);
  832. values.Add(value);
  833. }
  834. else
  835. {
  836. values[index] = value;
  837. }
  838. }
  839. }
  840. #endregion
  841. #region Private
  842. #region keys (Private)
  843. /// <summary>
  844. /// Keys
  845. /// </summary>
  846. private readonly List<TKey> keys;
  847. #endregion
  848. #region values (Private)
  849. /// <summary>
  850. /// Values
  851. /// </summary>
  852. private readonly List<TValue> values;
  853. #endregion
  854. #endregion
  855. #region Constructors
  856. /// <summary>
  857. /// Create test dictionary
  858. /// </summary>
  859. /// <param name="keys">Keys</param>
  860. /// <param name="values">Values</param>
  861. public TestDictionary(TKey[] keys, TValue[] values)
  862. {
  863. this.keys = new List<TKey>(keys);
  864. this.values = new List<TValue>(values);
  865. }
  866. #endregion
  867. #region GetEnumerator (Public)
  868. /// <summary>
  869. /// Get enumerator
  870. /// </summary>
  871. /// <returns>IEnumerator</returns>
  872. public override IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
  873. {
  874. for (int i = 0; i < keys.Count; ++i)
  875. {
  876. yield return new KeyValuePair<TKey, TValue>(keys[i], values[i]);
  877. }
  878. }
  879. #endregion
  880. #region TryGetValue (Public)
  881. /// <summary>
  882. /// Try get value
  883. /// </summary>
  884. /// <param name="key">Key</param>
  885. /// <param name="value">Value</param>
  886. /// <returns>
  887. /// True if getting the value with the key succeeded, false otherwise
  888. /// (the returned value will be the default value, mostly 0 or null).
  889. /// </returns>
  890. public override bool TryGetValue(TKey key, out TValue value)
  891. {
  892. int index = keys.IndexOf(key);
  893. if (index < 0)
  894. {
  895. value = default(TValue);
  896. return false;
  897. }
  898. else
  899. {
  900. value = values[index];
  901. return true;
  902. }
  903. }
  904. #endregion
  905. #region Remove (Public)
  906. /// <summary>
  907. /// Remove value at given key.
  908. /// </summary>
  909. /// <param name="key">Key</param>
  910. /// <returns>
  911. /// True if removing the key and value succeeded, false otherwise.
  912. /// </returns>
  913. public override bool Remove(TKey key)
  914. {
  915. int index = keys.IndexOf(key);
  916. if (index < 0)
  917. {
  918. return false;
  919. }
  920. else
  921. {
  922. keys.RemoveAt(index);
  923. values.RemoveAt(index);
  924. return true;
  925. }
  926. }
  927. #endregion
  928. #region Clear (Public)
  929. /// <summary>
  930. /// Clear
  931. /// </summary>
  932. public override void Clear()
  933. {
  934. keys.Clear();
  935. values.Clear();
  936. }
  937. #endregion
  938. }
  939. #endregion
  940. #endregion
  941. #region TestDictionaryBase (Static)
  942. /// <summary>
  943. /// Test dictionary base. Note: Too slow for a dynamic unit test.
  944. /// </summary>
  945. [Test]
  946. public static void TestDictionaryBase()
  947. {
  948. string[] testKeys = {
  949. "Whats", "up", "inda", "House"
  950. };
  951. int[] testValues = {
  952. 10, 2, 51, 20
  953. };
  954. TestDictionary<string, int> testDict =
  955. new TestDictionary<string, int>(testKeys, testValues);
  956. int allValues = 0;
  957. int allDictValues = 0;
  958. for (int num = 0; num < testValues.Length; num++)
  959. {
  960. allValues += testValues[num];
  961. }
  962. foreach (int someValue in testDict.Values)
  963. {
  964. allDictValues += someValue;
  965. }
  966. Assert.Equal(allValues, allDictValues);
  967. Assert.Equal(10, testDict["Whats"]);
  968. Assert.Equal(2, testDict["up"]);
  969. Assert.Equal(51, testDict["inda"]);
  970. Assert.Equal(20, testDict["House"]);
  971. }
  972. #endregion
  973. }
  974. }