2015-09-19 3 views
0

У меня проблема с реализацией пользовательского сопоставления в 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; } 
    } 
} 

Для компонента составного объекта I'm, используя один и тот же интерфейс.

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)); 
     } 
    } 
} 

Можете ли вы помочь мне с реали и перейдите к методу поиска? Это хороший способ или нет?

Очень спасибо

+0

В чем проблема? Добавьте второй аргумент в метод 'Find'' IEqualityComparer Comparer' или похожий на многие методы LINQ 'Func предикат' и используйте его внутри. В качестве побочного примечания поле 'holder' и текущая реализация' Composite.Find/Remove' довольно странные. –

+0

Вы также можете добавить ограничение IComparable на T, например, так что 'public interface IComponent где T: IComparable' – venerik

+0

Спасибо за ваш ответ. Да, это я знаю, но проблема в том, как реализовать этот компаратор. Потому что, я хочу сравнить ** string Name **, но у компаратора ** есть тип T **, поэтому метод сравнения будет также иметь тип T-входов. И я не могу реализовать object.Name, типа T. Это то, что я не понимаю. благодаря –

ответ

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(); 
    } 
} 

Две реализации Comparer будет:

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); 
Смежные вопросы