为UWP调整AsyncObservableCollection后出现奇怪的构建异常



我已经尝试将异步集合从Link调整为UWP的原始集合。但如果我试图构建文件,我会收到以下错误:

严重性代码描述项目文件行禁止显示状态错误无法确定集合类型"Logic.Model.AnconObservableCollection `1[DataAccess.Core.ch.Phex.neneneba API.Customer]"的项类型,因为它有多个Add方法或ICollection实现。若要使此集合类型在XAML中可用,请添加一个公共add(对象)方法,实现System.Collections.IList或单个System.Collections.Generic.ICollection.Ui.Windows

有人能给我一个提示吗?

致以最良好的问候Kaffi

源代码

    public delegate void OnMtCollectionChangedHandler(object sender, NotifyCollectionChangedEventArgs args);
[DataContract]
public  class AsyncObservableCollection<T> : ICollection<T>, IReadOnlyList<T>
{
    // ******************************************************************
    private List<T> _recordedNew = new List<T>();
    private List<T> _recordedRemoved = new List<T>();
    private bool _isRecording = false;
    private readonly object _syncRoot = new object();
    protected List<T> List = new List<T>();
    private readonly ObservableCollection<T> _obsColl = new ObservableCollection<T>();
    private readonly ConcurrentQueue<NotifyCollectionChangedEventArgs> _uiItemQueue = new ConcurrentQueue<NotifyCollectionChangedEventArgs>();
    public event OnMtCollectionChangedHandler OnMtCollectionChanged;
    public CoreDispatcher Dispatcher { get; set; }
    // ******************************************************************
    /// <summary>
    /// You should never add any item directly in the collection. 
    /// It should only serve as a readonly collection for the UI.
    /// If you ever decide to do so, it would be preferable to use directly the ObsCollection 
    /// without ever using this class (kind of detach)
    /// </summary>
    public ObservableCollection<T> ObsColl
    {
        get { return _obsColl; }
    }
    // ******************************************************************
    public AsyncObservableCollection()
    {
        //Dispatcher = Application.Current;
        Dispatcher = Window.Current.Dispatcher;
    }

    public AsyncObservableCollection(Collection<T> collection)
    {
        //Dispatcher = Application.Current;
        Dispatcher = Window.Current.Dispatcher;
        this.Add(collection.ToList<T>());
    }


    // ******************************************************************
    public bool IsRecording
    {
        get { return _isRecording; }
        set { _isRecording = value; }
    }
    // ******************************************************************
    /// <summary>
    /// Return tuple of new and removed items
    /// </summary>
    /// <returns></returns>
    public Tuple<List<T>, List<T>> ResetRecordedItems()
    {
        Tuple<List<T>, List<T>> changes;
        lock (_syncRoot)
        {
            changes = new Tuple<List<T>, List<T>>(_recordedNew, _recordedRemoved);
            _recordedNew = new List<T>();
            _recordedRemoved = new List<T>();
        }
        return changes;
    }
    // ******************************************************************
    public T[] GetCopyOfRecordedItemsNew()
    {
        T[] changes;
        lock (_syncRoot)
        {
            changes = _recordedNew.ToArray();
        }
        return changes;
    }
    // ******************************************************************
    public T[] GetCopyOfRecordedItemsRemoved()
    {
        T[] changes;
        lock (_syncRoot)
        {
            changes = _recordedRemoved.ToArray();
        }
        return changes;
    }
    // ******************************************************************
    private async void AddTask(NotifyCollectionChangedEventArgs args)
    {
        _uiItemQueue.Enqueue(args);
        // Dispatcher.BeginInvoke(new Action(this.ProcessQueue));
        await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
             {
                 this.ProcessQueue();
             });
    }
    // ******************************************************************
    private void ProcessQueue()
    {
        // This Method should always be invoked only by the UI thread only.
        if (!this.Dispatcher.HasThreadAccess)
        {
            throw new Exception("Can't be called from any thread than the dispatcher one");
        }
        NotifyCollectionChangedEventArgs args;
        while (this._uiItemQueue.TryDequeue(out args))
        {
            switch (args.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    int offset = 0;
                    foreach (T item in args.NewItems)
                    {
                        ObsColl.Insert(args.NewStartingIndex + offset, item);
                        offset++;
                    }
                    break;
                case NotifyCollectionChangedAction.Remove:
                    if (args.NewStartingIndex >= 0)
                    {
                        ObsColl.RemoveAt(args.NewStartingIndex);
                    }
                    else
                    {
                        foreach (T item in args.OldItems)
                        {
                            ObsColl.Remove(item);
                        }
                    }
                    break;
                case NotifyCollectionChangedAction.Replace:
                    // Replace is used for the [] operator. 'Insert' raise an 'Add' event.
                    if (args.NewStartingIndex >= 0 && args.OldStartingIndex < 0)
                    {
                        throw new ArgumentException(String.Format("Replace action expect NewStartingIndex and OldStartingIndex as: 0 <= {0} <= {1}, {2} <= 0.", args.NewStartingIndex, ObsColl.Count, args.OldStartingIndex));
                    }
                    IList listOld = args.OldItems as IList;
                    IList listNew = args.NewItems as IList;
                    if (listOld == null || listNew == null)
                    {
                        throw new ArgumentException("Both argument Old and New item should be IList in a replace action.");
                    }
                    ObsColl[args.NewStartingIndex] = (T)listNew[0];
                    break;
                case NotifyCollectionChangedAction.Reset:
                    ObsColl.Clear();
                    break;
                case NotifyCollectionChangedAction.Move:
                    ObsColl.Move(args.OldStartingIndex, args.NewStartingIndex);
                    break;
                default:
                    throw new Exception("Unsupported NotifyCollectionChangedEventArgs.Action");
            }
        }
    }
    // ******************************************************************
    public List<T> GetSnapshot()
    {
        List<T> listCopy = null;
        lock (_syncRoot)
        {
            listCopy = new List<T>(List);
        }
        return listCopy;
    }
    // ******************************************************************
    public void GetSnapshot(IList list)
    {
        lock (_syncRoot)
        {
            List.ApplyForEachItem((path) => list.Add(path));
        }
    }
    // ******************************************************************
    public virtual IEnumerator<T> GetEnumerator()
    {
        return GetSnapshot().GetEnumerator();
    }
    // ******************************************************************
    public virtual IEnumerator<T> GetBlockingEnumerator()
    {
        return new BlockingIterator<T>(List.GetEnumerator(), _syncRoot);
    }
    // ******************************************************************
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetSnapshot().GetEnumerator();
    }

    // ******************************************************************
    public void InsertAsFirst(T item)
    {
        NotifyCollectionChangedEventArgs args;
        lock (_syncRoot)
        {
            List.Insert(0, item);
            args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, 0);
            AddTask(args);
        }
        RaiseEventCollectionChanged(args);
    }
    // ******************************************************************
    public void Add(T item)
    {
        NotifyCollectionChangedEventArgs args;
        lock (_syncRoot)
        {
            List.Add(item);
            if (_isRecording)
            {
                _recordedNew.Add(item);
            }
            args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, List.Count - 1);
            AddTask(args);
        }
        RaiseEventCollectionChanged(args);
    }
    public void Add(IList<T> items)
    {
        NotifyCollectionChangedEventArgs args;
        lock (_syncRoot)
        {
            int insertIndex = List.Count;
            List.AddRange(items);
            if (_isRecording)
            {
                _recordedNew.AddRange(items);
            }
            args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, items as IList, insertIndex);
            AddTask(args);
        }
        RaiseEventCollectionChanged(args);
    }
    // ******************************************************************
    public bool Remove(T item)
    {
        bool isRemoved = false;
        NotifyCollectionChangedEventArgs args;
        lock (_syncRoot)
        {
            isRemoved = List.Remove(item);
            if (_isRecording)
            {
                _recordedNew.Add(item);
            }
            args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item);
            AddTask(args);
        }
        RaiseEventCollectionChanged(args);
        return isRemoved;
    }
    // ******************************************************************
    public void Replace(T itemOld, T itemNew)
    {
        NotifyCollectionChangedEventArgs args = null;
        lock (_syncRoot)
        {
            int index = List.IndexOf(itemOld);
            if (index < 0 || index >= List.Count)
            {
                throw new ArgumentException("Invalid old value");
            }
            if (_isRecording)
            {
                _recordedNew.Add(itemNew);
                _recordedRemoved.Add(itemOld);
            }
            List[index] = itemNew;
            args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, itemNew, itemOld, index);
            AddTask(args);
        }
        RaiseEventCollectionChanged(args);
    }
    // ******************************************************************
    private void RaiseEventCollectionChanged(NotifyCollectionChangedEventArgs args)
    {
        if (OnMtCollectionChanged != null && args != null)
        {
            OnMtCollectionChanged(this, args);
        }
    }
    // ******************************************************************
    /// <summary>
    /// To use this function and all 'Unsafe' ones in a MT context, 
    /// you should have a lock on the collection prior to call it.
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    public T UnsafeGetAt(int index)
    {
        return List[index];
    }
    // ******************************************************************
    /// <summary>
    /// To use this function and all 'Unsafe' ones in a MT context, 
    /// you should have a lock on the collection prior to call it.
    /// </summary>
    /// <param name="index"></param>
    /// <param name="item"></param>
    /// <returns></returns>
    public T UnsafeSetAt(int index, T itemNew)
    {
        T itemOld = List[index];
        if (_isRecording)
        {
            _recordedNew.Add(itemNew);
            _recordedRemoved.Add(itemOld);
        }
        List[index] = itemNew;
        NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, itemNew, itemOld, index);
        AddTask(args);
        RaiseEventCollectionChanged(args);
        return itemOld;
    }
    // ******************************************************************
    public void UnsafeInsertAt(int index, T itemNew)
    {
        if (_isRecording)
        {
            _recordedNew.Add(itemNew);
        }
        List.Insert(index, itemNew);
        NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, itemNew, index);
        AddTask(args);
        RaiseEventCollectionChanged(args);
    }
    // ******************************************************************
    /// <summary>
    /// To use this function and all 'Unsafe' ones in a MT context, 
    /// you should have a lock on the collection prior to call it.
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    public T UnsafeRemoveAt(int index)
    {
        T itemOld = List[index];
        if (_isRecording)
        {
            _recordedRemoved.Add(itemOld);
        }
        List.RemoveAt(index);
        NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, itemOld, index);
        AddTask(args);
        RaiseEventCollectionChanged(args);
        return itemOld;
    }

    // ******************************************************************
    public virtual void Clear()
    {
        NotifyCollectionChangedEventArgs args = null;
        lock (_syncRoot)
        {
            if (_isRecording)
            {
                _recordedRemoved.AddRange(List);
            }
            List.Clear();
            args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
            AddTask(args);
        }
        RaiseEventCollectionChanged(args);
    }
    // ******************************************************************
    public bool Contains(T item)
    {
        bool result;
        lock (_syncRoot)
        {
            result = List.Contains(item);
        }
        return result;
    }
    // ******************************************************************
    public void CopyTo(T[] array, int arrayIndex)
    {
        lock (_syncRoot)
        {
            List.CopyTo(array, arrayIndex);
        }
    }
    // ******************************************************************
    public int Count
    {
        get
        {
            lock (_syncRoot)
            {
                return List.Count;
            }
        }
    }
    // ******************************************************************
    public void Remove(object item)
    {
        Remove((T)item);
    }
    // ******************************************************************
    public int IndexOf(object value)
    {
        return IndexOf((T)value);
    }

    // ******************************************************************
    public object SyncRoot
    {
        get { return _syncRoot; }
    }
    // ******************************************************************
    public bool IsEqual(IEnumerable<T> iEnumerable)
    {
        if (this.Count != iEnumerable.Count())
        {
            return false;
        }
        lock (_syncRoot)
        {
            var thisEnumerator = this.GetEnumerator();
            thisEnumerator.Reset();
            foreach (var t in iEnumerable)
            {
                thisEnumerator.MoveNext();
                if (thisEnumerator.Current.Equals(t))
                {
                    return false;
                }
            }
            Disposal.Dispose(thisEnumerator);
        }
        return true;
    }
    // ******************************************************************
    private void IsEqualToObsColl()
    {
        if (!IsEqual(this.ObsColl))
        {
            Dump();
        }
    }
    // ******************************************************************
    /// <summary>
    /// This function dumps to the ouput window formated lines of the content of both collections...
    /// The list which is thread safe and the obs coll that is used as a readonly list. 
    /// Its main purpose is to debug to validate that both list contains the same values in the same order.
    /// </summary>
    private void Dump()
    {
        Debug.WriteLine("=============== Start");
        lock (_syncRoot)
        {
            IEnumerator enum1 = List.GetEnumerator();
            IEnumerator enum2 = ObsColl.GetEnumerator();
            enum1.Reset();
            enum2.Reset();
            bool ok1 = enum1.MoveNext();
            bool ok2 = enum2.MoveNext();
            while (ok1 || ok2)
            {
                Debug.WriteLine(String.Format("{0,20} - {0,-20}", ok1 == true ? enum1.Current : "-", ok2 == true ? enum2.Current : "-"));
                if (ok1)
                    ok1 = enum1.MoveNext();
                if (ok2)
                    ok2 = enum2.MoveNext();
            }
            Disposal.Dispose(enum1);
            Disposal.Dispose(enum2);
        }
        Debug.WriteLine("=============== End");
    }

    // ******************************************************************
    [OnSerializing]
    void OnSerializing(StreamingContext ctx)
    {
        Monitor.Enter(this._syncRoot);
    }
    // ******************************************************************
    [OnSerialized]
    void OnSerialized(StreamingContext ctx)
    {
        Monitor.Exit(this._syncRoot);
    }
    // ******************************************************************
    [OnDeserializing]
    void OnDeserializing(StreamingContext ctx)
    {
    }
    // ******************************************************************
    [OnDeserialized]
    void OnDeserialized(StreamingContext ctx)
    {
    }
    // ******************************************************************
    /// <summary>
    /// ATTENTION : This method is not MT safe
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    public T this[int index]
    {
        get { return this.List[index]; }
    }
    // ******************************************************************
    /// <summary>
    /// Add stack functionnality to use the list as a queue
    /// </summary>
    /// <param name="item"></param>
    public void Push(T item)
    {
        Add(item);
    }
    // ******************************************************************
    /// <summary>
    /// Add stack functionnality to use the list as a queue
    /// </summary>
    /// <returns></returns>
    public bool TryPop(out T item)
    {
        lock (_syncRoot)
        {
            int count = List.Count;
            if (count > 0)
            {
                item = UnsafeRemoveAt(count - 1);
                return true;
            }
        }
        item = default(T);
        return false;
    }
    // ******************************************************************
    /// <summary>
    /// Add queue functionnality to use the list as a queue. Item are added at the end of the list
    /// </summary>
    /// <param name="item"></param>
    public void Enqueue(T item)
    {
        Add(item);
    }
    // ******************************************************************
    /// <summary>
    ///  Add queue functionnality to use the list as a queue. Item are removed at position 0 (cost a lot due to move all array item left from one position)
    /// </summary>
    /// <returns></returns>
    public bool TryDequeue(out T item)
    {
        lock (_syncRoot)
        {
            int count = List.Count;
            if (count > 0)
            {
                item = UnsafeRemoveAt(0);
                return true;
            }
        }
        item = default(T);
        return false;
    }
    // ******************************************************************
    public bool IsReadOnly
    {
        get { return false; }
    }
    // ******************************************************************
    bool ICollection<T>.Remove(T item)
    {
        return Remove(item);
    }
    // ******************************************************************
    public void CopyTo(Array array, int index)
    {
        lock (_syncRoot)
        {
            foreach (var t in List)
            {
                array.SetValue(t, index++);
            }
        }
    }
    // ******************************************************************
    public bool IsSynchronized
    {
        get { return Dispatcher.HasThreadAccess; }
    }
    // ******************************************************************
}

您在代码示例中遗漏了一些内容,所以我无法编译它(DisposalApplyForEachItem),但我可以看到您也有一个public void Add(T item)public void Add(IList<T> items)方法。ICollection<T>接口实现只需要第一个——对于第二个,通常的命名约定是AddRange

最新更新