// retrieve the value, using the cache if necessary internal object GetValue(object item, PropertyDescriptor pd, bool indexerIsNext) { if (!ShouldCache(item, pd)) { // normal case - just get the value the old-fashioned way return(pd.GetValue(item)); } else { // lazy creation of the cache if (_table == null) { _table = new HybridDictionary(); } // look up the value in the cache bool isXLinqCollectionProperty = SystemXmlLinqHelper.IsXLinqCollectionProperty(pd); ValueTableKey key = new ValueTableKey(item, pd); object value = _table[key]; Action FetchAndCacheValue = () => { // if there's no entry, fetch the value and cache it if (value == null) { if (SystemDataHelper.IsDataSetCollectionProperty(pd)) { // intercept GetValue calls for certain ADO properties value = SystemDataHelper.GetValue(item, pd, !FrameworkAppContextSwitches.DoNotUseFollowParentWhenBindingToADODataRelation); } else if (isXLinqCollectionProperty) { // interpose our own value for special XLinq properties value = new XDeferredAxisSource(item, pd); } else { value = pd.GetValue(item); } if (value == null) { value = CachedNull; // distinguish a null value from no entry } // for DataSet properties, store a weak reference to avoid // a memory leak if (SystemDataHelper.IsDataSetCollectionProperty(pd)) { value = new WeakReference(value); } _table[key] = value; } if (SystemDataHelper.IsDataSetCollectionProperty(pd)) { // we stored a weak reference - decode it now WeakReference wr = value as WeakReference; if (wr != null) { value = wr.Target; } } }; FetchAndCacheValue(); if (value == null) { // we can only get here if we cached a weak reference // whose target has since been GC'd. Repeating the call // will refetch the value from the item, and is guaranteed // to set value to non-null. FetchAndCacheValue(); } // decode null, if necessary if (value == CachedNull) { value = null; } else if (isXLinqCollectionProperty && !indexerIsNext) { // The XLinq properties need special help. When the path // contains "Elements[Foo]", we should return the interposed // XDeferredAxisSource; the path worker will then call the XDAS's // indexer with argument "Foo", and obtain the desired // ObservableCollection. But when the path contains "Elements" // with no indexer, we should return an ObservableCollection // corresponding to the full set of children. // [All this applies to "Descendants" as well.] XDeferredAxisSource xdas = (XDeferredAxisSource)value; value = xdas.FullCollection; } return(value); } }