泛型基类和扩展方法的具体实现

发布于 2024-09-14 16:18:00 字数 5317 浏览 12 评论 0原文

这篇文章的最终目标是重写通用基类的具体实现的 ToString() 方法,同时仍然能够使用 Linq 扁平化技术搜索实现。因此,如果您阅读本文并发现更好的方法,请告诉我。我正在使用 Silverlight 的 Telerik 控件,它们不会更改其 api 以允许某些控件属性进行数据绑定,而是依赖于它们绑定到的任何对象的 ToString() 方法。是的,愚蠢的..无论如何,这就是我所拥有的。

我的页面上的 RadTreeView 控件。树视图中每个节点的 FullPath 属性使用其绑定到的每个项目的 ToString() 方法(所以这是我需要重写的)。

我必须创建一个“中间”类来增强我的基本模型类,以便它可以作为树视图中的层次结构进行绑定,然后使用该通用类的具体实现来重写 ToString()。现在的问题是我的 Linq 扩展会爆炸,因为它无法将具体实现转换回基本泛型类。我喜欢仿制药,但这对我来说太多了。 需要帮助解决扩展方法问题。

中间泛型类:

public class HeirarchicalItem<T> : NotifyPropertyChangedBase, INotifyCollectionChanged where T : class
{
    public event NotifyCollectionChangedEventHandler CollectionChanged;

    public virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs ea)
    {
        if (CollectionChanged != null)
            CollectionChanged(this, ea);
    }

    public HeirarchicalItem() { }

    public HeirarchicalItem(T item)
    {
        Item = item;
    }

    public HeirarchicalItem(IEnumerable<T> collection)
    {
        CopyFrom(collection);
    }

    private T _item;
    public T Item
    {
        get
        {
            return _item;
        }
        set
        {
            _item = value;
            RaisePropertyChanged<HeirarchicalItem<T>>(a => a.Item);
        }
    }

    private ObservableCollection<HeirarchicalItem<T>> _children = new ObservableCollection<HeirarchicalItem<T>>();
    public virtual ObservableCollection<HeirarchicalItem<T>> Children
    {
        get { return _children; }
        set
        {
            _children = value;
            RaisePropertyChanged<HeirarchicalItem<T>>(a => a.Children);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }
    }

    private void CopyFrom(IEnumerable<T> collection)
    {
        if ((collection != null))
        {
            using (IEnumerator<T> enumerator = collection.GetEnumerator())
            {
                while (enumerator.MoveNext())
                {
                    HeirarchicalItem<T> newHeirarchicalItem = new HeirarchicalItem<T>(enumerator.Current);
                    Children.Add(newHeirarchicalItem);
                    RaisePropertyChanged<HeirarchicalItem<T>>(a => a.Children);
                    OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add));
                }
            }
        }
    }
}

基础模型类:(使用此方法将数据往返于 WCF Ria 服务) class)

public class tbl_Path : EntityBase, IFullPath, IEquatable<tbl_Path>, IEqualityComparer<tbl_Path>
{
    public tbl_Path();
    public int GetHashCode(tbl_Path obj);
    public override string ToString();

    public DateTime CreateDate { get; set; }
    public short Depth { get; set; }
    public string FullPath { get; set; }
    public bool IsAuthorized { get; set; }
    public bool IsSelected { get; set; }
    public string Name { get; set; }
    public override IEnumerable<Operation> Operations { get; }
    public int? ParentPathID { get; set; }
    public int PathID { get; set; }
    public Guid SecurityKey { get; set; }
    public EntityCollection<tbl_Configuration> tbl_Configuration { get; set; }
    public EntityCollection<tbl_Key> tbl_Key { get; set; }
    public EntityCollection<tbl_SecurityACL> tbl_SecurityACL { get; set; }
    public EntityCollection<tbl_SecurityInheriting> tbl_SecurityInheriting { get; set; }
    public EntityCollection<tbl_Variable> tbl_Variable { get; set; }
}

具体实现,以便我可以重写 ToString():

public class HeirarchicalPath : HeirarchicalItem<tbl_Path>
{
    public HeirarchicalPath()
    {

    }
    public HeirarchicalPath(tbl_Path item)
        : base(item)
    {

    }
    public HeirarchicalPath(IEnumerable<tbl_Path> collection)
        : base(collection)
    {

    }

    public override string ToString()
    {
        return Item.Name; **// we override here so Telerik is happy**
    }
}

最后,这是在编译时爆炸的 Linq 扩展方法,因为我引入了通用基类的具体实现。

    public static IEnumerable<T> Traverse<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> fnRecurse)
    {
        foreach (T item in source)
        {
            yield return item;

            IEnumerable<T> seqRecurse = fnRecurse(item);

            if (seqRecurse != null)
            {
                foreach (T itemRecurse in Traverse(seqRecurse, fnRecurse))
                {
                    yield return itemRecurse;
                }
            }
        }
    }

实际破坏的代码:(x.Children 突出显示错误)

Cannot implicitly convert type 
'System.Collections.ObjectModel.ObservableCollection<HeirarchicalItem<tbl_Path>>' to 
'System.Collections.Generic.IEnumerable<HeirarchicalPath>'. An explicit conversion 
exists (are you missing a cast?)


            HeirarchicalPath currentItem = this.Paths.Traverse(x => x.Children).Where(x => x.Item.FullPath == "$/MyFolder/Hello").FirstOrDefault();

The end goal for this post is to override the ToString() method of a concrete implementation of a generic base class while still being able to search the implementation using Linq flattening technique. So if you read this and see a better way let me know. I'm using Telerik controls for Silverlight and they won't change their api to allow some of their control properties to be data-bound and instead rely on the ToString() method of whatever object they are bound to. yea, stupid.. Anyway here is what I've got.

RadTreeView control on my page. The FullPath property of each node in the treeview uses the ToString() method of each item its bound to (so this is what I need to override).

I had to create an "intermediary" class to enhance my base model class so it can be bound as a heirarchy in the tree view and then a concrete implementation of that generic class to override ToString(). Now the problem is I have a Linq extension that explodes because it cannot convert the concrete implementation back to the base generic class. I love generics but this is too much for me. Need help on solving the extension method issue.

Intermediary generic class:

public class HeirarchicalItem<T> : NotifyPropertyChangedBase, INotifyCollectionChanged where T : class
{
    public event NotifyCollectionChangedEventHandler CollectionChanged;

    public virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs ea)
    {
        if (CollectionChanged != null)
            CollectionChanged(this, ea);
    }

    public HeirarchicalItem() { }

    public HeirarchicalItem(T item)
    {
        Item = item;
    }

    public HeirarchicalItem(IEnumerable<T> collection)
    {
        CopyFrom(collection);
    }

    private T _item;
    public T Item
    {
        get
        {
            return _item;
        }
        set
        {
            _item = value;
            RaisePropertyChanged<HeirarchicalItem<T>>(a => a.Item);
        }
    }

    private ObservableCollection<HeirarchicalItem<T>> _children = new ObservableCollection<HeirarchicalItem<T>>();
    public virtual ObservableCollection<HeirarchicalItem<T>> Children
    {
        get { return _children; }
        set
        {
            _children = value;
            RaisePropertyChanged<HeirarchicalItem<T>>(a => a.Children);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }
    }

    private void CopyFrom(IEnumerable<T> collection)
    {
        if ((collection != null))
        {
            using (IEnumerator<T> enumerator = collection.GetEnumerator())
            {
                while (enumerator.MoveNext())
                {
                    HeirarchicalItem<T> newHeirarchicalItem = new HeirarchicalItem<T>(enumerator.Current);
                    Children.Add(newHeirarchicalItem);
                    RaisePropertyChanged<HeirarchicalItem<T>>(a => a.Children);
                    OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add));
                }
            }
        }
    }
}

Base model class: (data is shuttled to and from WCF Ria service using this class)

public class tbl_Path : EntityBase, IFullPath, IEquatable<tbl_Path>, IEqualityComparer<tbl_Path>
{
    public tbl_Path();
    public int GetHashCode(tbl_Path obj);
    public override string ToString();

    public DateTime CreateDate { get; set; }
    public short Depth { get; set; }
    public string FullPath { get; set; }
    public bool IsAuthorized { get; set; }
    public bool IsSelected { get; set; }
    public string Name { get; set; }
    public override IEnumerable<Operation> Operations { get; }
    public int? ParentPathID { get; set; }
    public int PathID { get; set; }
    public Guid SecurityKey { get; set; }
    public EntityCollection<tbl_Configuration> tbl_Configuration { get; set; }
    public EntityCollection<tbl_Key> tbl_Key { get; set; }
    public EntityCollection<tbl_SecurityACL> tbl_SecurityACL { get; set; }
    public EntityCollection<tbl_SecurityInheriting> tbl_SecurityInheriting { get; set; }
    public EntityCollection<tbl_Variable> tbl_Variable { get; set; }
}

Concrete Implementation so that I can override ToString():

public class HeirarchicalPath : HeirarchicalItem<tbl_Path>
{
    public HeirarchicalPath()
    {

    }
    public HeirarchicalPath(tbl_Path item)
        : base(item)
    {

    }
    public HeirarchicalPath(IEnumerable<tbl_Path> collection)
        : base(collection)
    {

    }

    public override string ToString()
    {
        return Item.Name; **// we override here so Telerik is happy**
    }
}

And finally here is the Linq extension method that explodes during compile time because I introduced a concrete implementation of my generic base class.

    public static IEnumerable<T> Traverse<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> fnRecurse)
    {
        foreach (T item in source)
        {
            yield return item;

            IEnumerable<T> seqRecurse = fnRecurse(item);

            if (seqRecurse != null)
            {
                foreach (T itemRecurse in Traverse(seqRecurse, fnRecurse))
                {
                    yield return itemRecurse;
                }
            }
        }
    }

Actual code that is breaking: (x.Children is highlighted with the error)

Cannot implicitly convert type 
'System.Collections.ObjectModel.ObservableCollection<HeirarchicalItem<tbl_Path>>' to 
'System.Collections.Generic.IEnumerable<HeirarchicalPath>'. An explicit conversion 
exists (are you missing a cast?)


            HeirarchicalPath currentItem = this.Paths.Traverse(x => x.Children).Where(x => x.Item.FullPath == "$/MyFolder/Hello").FirstOrDefault();

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

故事↓在人 2024-09-21 16:18:00

想通了。在发布问题后我一整天都在研究这个问题,我一如既往地解决了它。

只需将这一点添加到我的具体实现中,就不会再出现编译器错误。

    private ObservableCollection<HeirarchicalPath> _children = new ObservableCollection<HeirarchicalPath>();
    public new ObservableCollection<HeirarchicalPath> Children
    {
        get
        {
            return _children;
        }
        set
        {

            if (value == null)
                return;

            _children = value;
            RaisePropertyChanged<HeirarchicalPath>(a => a.Children);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }
    }

Figured it out. Been working on this all day and minutes after posting the question I resolve it as always.

Just needed to add this bit to my concrete implementation and no more compiler errors.

    private ObservableCollection<HeirarchicalPath> _children = new ObservableCollection<HeirarchicalPath>();
    public new ObservableCollection<HeirarchicalPath> Children
    {
        get
        {
            return _children;
        }
        set
        {

            if (value == null)
                return;

            _children = value;
            RaisePropertyChanged<HeirarchicalPath>(a => a.Children);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文