使用自定义比较器

时间:2015-09-19 12:41:25

标签: c# generics

我在Composite C#中实现自定义比较器时遇到问题。我想传递自定义比较器我的Composite对象。

这是IComponent接口:

namespace composite
{
    public interface IComponent<T>
    {
        void Add(IComponent<T> component);
        IComponent<T> Find(T item);
        IComponent<T> Remove(T item);
        string Display(int depth);
        string Name { get; set; }
    }
}

对于Component,Composite对象我使用相同的接口。

复合(我的组件集合)

namespace composite
{
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using System.Text;

    public class Composite<T> : IComponent<T>
    {
        private ICollection<IComponent<T>> components;    
        private IComponent<T> holder;

        public Composite(string name)
        {
            this.Name = name;
            this.holder = null;
            this.components = new List<IComponent<T>>();
        }

        public string Name { get; set; }

        public void Add(IComponent<T> component)
        {
            this.components.Add(component);
        }

        public IComponent<T> Find(T item)
        {
            this.holder = this;
            if (item.Equals(this.Name))
            {
                return this;
            }

            IComponent<T> found = null;

            //this.components.Select(s => s.Name == item)

            foreach (IComponent<T> component in this.components)
            {
                found = component.Find(item);
                if (found != null)
                {
                    break;
                }
            }
            return found;
        }

        public IComponent<T> Remove(T item)
        {
            this.holder = this;
            IComponent<T> p = holder.Find(item);
            if (this.holder != null)
            {
                (this.holder as Composite<T>).components.Remove(p);
                return this.holder;
            }
            else
            {
                return this;
            }
        }

        //public IEnumerable<IComponent<T>> Dsiplay(int depth)    

        public string Display(int depth)
        {
            StringBuilder s = new StringBuilder();
            s.Append("set " + this.Name + " length :" + this.components.Count + "\n");
            foreach (IComponent<T> component in components)
            {
                s.Append(component.Display(depth + 2));
            }
            return s.ToString();
        }
    }
}

组件:

namespace composite
{
    using System;
    using System.Collections.Generic;

    public class Component<T> : IComponent<T>
    {
        public Component(string name)
        {
            this.Name = name;
        }

        public string Display(int depth)
        {
            return new string('-', depth) + this.Name + "\n";
        }

        public string Name { get; set; }

        public IComparer<T> MyComparer { get; private set; }

        //public IComparer<T> myComparer { set; }

        public void Add(IComponent<T> item)
        { 
            throw new NotImplementedException();
        }

        public IComponent<T> Find(T item)
        {

            //Here I want to use comparer object, instead of Equals
            //Something like this:

            //return MyComparer.Compare( ... ) == 0 ? this : null;

            return item.Equals(this.Name) ? this : null;
        }

        public IComponent<T> Remove(T item)
        {
            throw new NotImplementedException();
        }
    }
}

最后我的主要功能是:

namespace composite
{
    class Program
    {
        static void Main(string[] args)
        {
            //Creating custom comparer and pass to the constructor composite?
            IComparer<string> myComparer = new ...


            IComponent<string> comp1 = new Component<string>("Komponenta 1");
            IComponent<string> comp2 = new Component<string>("Komponenta 2");
            IComponent<string> comp3 = new Component<string>("Komponenta 3");
            IComponent<string> comp4 = new Component<string>("Komponenta 4");
            IComponent<string> comp5 = new Component<string>("Komponenta 5");

            IComponent<string> composite = new Composite<string>("Composite 1");

            IComponent<string> composite2 = new Composite<string>("Composite 2");

            composite.Add(comp1);
            composite.Add(comp2);
            composite.Add(comp3);
            composite.Add(comp4);
            composite.Add(comp5);

            composite2.Add(comp1);
            composite2.Add(comp2);
            composite2.Add(comp3);
            composite2.Add(comp4);
            composite2.Add(comp5);

            composite.Add(composite2);

            Console.Write(composite.Display(0));
        }
    }
}

你能帮我实现自定义比较器并传递给Find方法吗? 是不是好方法?

非常感谢

1 个答案:

答案 0 :(得分:0)

这里的主要问题是:

  • 组件具有Name属性
  • 组件不使用泛型类型参数T
  • Find方法获取类型为T的通用项目以进行查找和比较 它带有组件名称 - 这只适用于IComponent<string>

因此,最简单的(也是我首选的)解决方案是摆脱比较器。 相反,我会用谓词定义find方法(如评论中所述)。

但你要求比较器!

因此需要进行一些调整:

  • T Data {get; set;}中定义属性IComponent<T>Component<T>Composite<T>

  • 中提供实施方案
  • 如果您愿意,可以从IComparer<T>切换到IEqualityComparer<IComponent<T>> 因为你想搜索相等的组件而不是比较元素

  • 相应地更改删除方法

在代码中(缩短一点):

public interface IComponent<T>
{
    void Add(IComponent<T> component);
    IComponent<T> Find(IComponent<T> item, IEqualityComparer<IComponent<T>> comparer);
    IComponent<T> Find(Predicate<IComponent<T>> condition)
    bool Remove(IComponent<T> item, IEqualityComparer<IComponent<T>> comparer);
    string Display(int depth);
    string Name { get; set; }
    T Data { get; set; }
}

public class Component<T> : IComponent<T>
{

    public T Data { get; set; }
    public string Name { get; set; }

    public Component(string name)
        => Name = name;

    public string Display(int depth) =>
        new string('-', depth) + Name + "\n";

    public IComponent<T> Find(IComponent<T> item, IEqualityComparer<IComponent<T>> comparer)
        => comparer.Equals(item, this) ? this : null;

    public IComponent<T> Find(Predicate<IComponent<T>> condition)
        => condition(this) ? this : null;

    public void Add(IComponent<T> item)
        => throw new InvalidOperationException();

    public bool Remove(IComponent<T> item, IEqualityComparer<IComponent<T>> comparer)
        => throw new InvalidOperationException();

}    

public class Composite<T> : IComponent<T>
{
    private IList<IComponent<T>> components = new List<IComponent<T>>();
    public T Data { get; set; }
    public string Name { get; set; }

    public Composite(string name)
        => Name = name;

    public void Add(IComponent<T> component)
        => components.Add(component);

    public IComponent<T> Find(IComponent<T> item, IEqualityComparer<IComponent<T>> comparer)
    {
        if (comparer.Equals(item, this))
            return this;
        else
            foreach (var component in components)
            {
                var childItem = component.Find(item, comparer);
                if (childItem != null)
                    return childItem;
            }
        return null;
    }

    public bool Remove(IComponent<T> item, IEqualityComparer<IComponent<T>> comparer)
    {
        var result = false;
        for (var i = components.Count - 1; i >= 0; i--)
            if (comparer.Equals(components[i], item))
            {
                components.RemoveAt(i);
                result = true;
            }

        return result;
    }

    public IComponent<T> Find(Predicate<IComponent<T>> condition)
    {
        if (condition(this))
            return this;
        foreach (var item in components)
        {
            var result = item.Find(condition);
            if (result != null)
                return result;
        }
        return null;
    }

    public string Display(int depth)
    {
        var s = new StringBuilder();
        s.Append(new string('-', depth) + "set " + Name + " length :" + components.Count + "\n");
        foreach (var component in components)
            s.Append(component.Display(depth + 2));
        return s.ToString();
    }
}

两个比较器实现将是:

public class DefaultComparer<T> : IEqualityComparer<IComponent<T>>
{

    public bool Equals(IComponent<T> x, IComponent<T> y)
        => EqualityComparer<T>.Default.Equals(x.Data, y.Data);

    public int GetHashCode(IComponent<T> obj)
        => EqualityComparer<T>.Default.GetHashCode(obj.Data);

}

public class NameComparer<T> : IEqualityComparer<IComponent<T>>
{
    public bool Equals(IComponent<T> x, IComponent<T> y)
        => string.Equals(x.Name, y.Name);

    public int GetHashCode(IComponent<T> obj)
        => (obj.Name ?? string.Empty).GetHashCode();
}

如何使用?

如果要搜索具有给定名称的组件,现在可以使用:

        var element1 = composite.Find(new Component<string>("Komponenta 5"), new NameComparer<string>());
        Console.WriteLine(element1.Name);

或事件更简单:

        var element2 = composite.Find(t => string.Equals(t.Name, "Komponenta 5"));
        Console.WriteLine(element2.Name);