Как быстро искать объекты определенного класса или дочерних классов в C # - PullRequest
0 голосов
/ 27 января 2019

Я хочу создать коллекцию объектов различных классов и иметь возможность быстрого поиска всех экземпляров, которые могут быть назначены определенному классу, без необходимости повторять весь список.Я могу использовать Dictionary<System.Type, List<object>>, однако, это не найдет мне все дочерние классы / интерфейсы.

public class Parent {
}

public class Child : Parent {
}

public class Other {
}


public class ObjHolder {
    Dictionary<System.Type, List<object>> objs = new Dictionary<System.Type, List<object>>();

    public void AddObject(object obj) {
        if (!objs.ContainsKey(obj.GetType()) {
            objs[obj.GetType()] = new List<object>();
        }

        objs[obj.GetType()].Add(obj);
    }

    public List<object> GetObjectsOfType<T>() {
        return objs.ContainsKey(typeof(T)) ? objs[typeof(T)] : new List<object>();
    }
}

Теперь это будет прекрасно работать для следующего:

ObjHolder o = new ObjHolder();
o.AddObject(new Parent());
o.AddObject(new Other());
o.GetObjectsOfType<Parent>(); // Returns only the Parent object    

Но этоне будет работать в следующем случае:

ObjHolder o = new ObjHolder();
o.AddObject(new Child());
o.AddObject(new Other());
o.GetObjectsOfType<Parent>(); // Returns an empty list

Я хочу иметь возможность захватывать все объекты, которые могут быть назначены для Parent, включая объект Child, но код не будет его возвращать.

Есть идеи, как сделать это эффективно?

Ответы [ 4 ]

0 голосов
/ 28 января 2019

Итак, вы хотите что-то похожее на словарь.Давайте попробуем реализовать это шаг за шагом.Мой код сильно прокомментирован, поэтому я позволю ему говорить.По сути, все, что вам нужно сделать, это оставить хлебные крошки по пути, когда вы добавляете свой объект в держатель объекта.

public class InheritanceInfo
{
    private class InheritanceChain
    {
        /// <summary>
        /// LinkedList which holds inheritance chain from least derived to most derived for a given Type.
        /// </summary>
        private readonly LinkedList<Type> _linkedList = new LinkedList<Type>();

        /// <summary>
        /// Creates an Inheritance chain for a given type which holds information about base types.
        /// </summary>
        /// <param name="t">Type for which inheritance chain will be created.</param>
        public InheritanceChain(Type t)
        {
            Type type = t;
            do
            {
                if (type == typeof(object))
                {
                    break;
                }
                _linkedList.AddFirst(type);
                type = type.BaseType;
            } while (true);
        }

        /// <summary>
        /// First element of LinkedList. This will be used for iteration.
        /// </summary>
        public LinkedListNode<Type> First => _linkedList.First;
    }

    /// <summary>
    /// Dictionary which holds Type vs DerivedTypes information. Basically does the all handling.
    /// </summary>
    private readonly ConcurrentDictionary<Type, SingleStepDerivedTypes> _inheritanceDictionary;

    /// <summary>
    /// InheritanceInfo class holds information about each Type's inheritance tree.
    /// Each Type holds information about one step down the inheritance tree.
    /// Example: public class C:B{}
    ///          public class B:A{}
    ///          public class A  {}
    /// Inheritance infor for class A holds info about only B because C is derived from B and
    /// it is not a direct descendant of A.
    /// </summary>
    public InheritanceInfo() {
        _inheritanceDictionary = new ConcurrentDictionary<Type, SingleStepDerivedTypes>();
    }

    /// <summary>
    /// Updates the given Type inheritance tree info.
    /// </summary>
    /// <param name="type"></param>
    public void Update(Type type) {
        var element = new InheritanceChain(type).First;
        while (element.Next != null) {
            _inheritanceDictionary.AddOrUpdate(element.Value, (_)=>AddValueFactory(element.Next.Value), (_,sdt)=>UpdateValueFactory(element.Next.Value,sdt));
            element = element.Next;
        }
    }

    /// <summary>
    /// Gets all the assignable types for the given type t.
    /// </summary>
    /// <param name="t">Type for which assignable types will be searched.</param>
    /// <returns>All the assignable types for Type t.</returns>
    public IEnumerable<Type> GetAssignables(Type t)
    {
        if(_inheritanceDictionary.TryGetValue(t ,out var derivedTypes) == false) {
            return Array.Empty<Type>();
        }
        var recursive = derivedTypes.GetTypes().SelectMany(tp=>GetAssignables(tp));
        return recursive.Concat(derivedTypes.GetTypes());
    }

    /// <summary>
    /// Add value to the dictionary
    /// </summary>
    /// <param name="t">Type to add to ConcurrentDictionary</param>
    /// <returns>SingleStepDerivedTypes which holds information about derived type t</returns>
    private static SingleStepDerivedTypes AddValueFactory(Type t) {
        var s = new SingleStepDerivedTypes();
        s.Add(t);
        return s;
    }

    /// <summary>
    /// Updates the already created SingleStepDerivedTypes object.
    /// </summary>
    /// <param name="t">Type to add</param>
    /// <param name="sdt">SingleStepDerivedTypes</param>
    /// <returns>Updated SingleStepDerivedTypes.</returns>
    private static SingleStepDerivedTypes UpdateValueFactory(Type t, SingleStepDerivedTypes sdt) {
        sdt.Add(t);
        return sdt;
    }
}



public class SingleStepDerivedTypes
{
    /// <summary>
    /// HashSet which holds information about derived Types.
    /// </summary>
    private readonly HashSet<Type> _singleStepDerivedTypes;

    /// <summary>
    /// Constructor ;)
    /// </summary>
    public SingleStepDerivedTypes() {
        _singleStepDerivedTypes = new HashSet<Type>();
    }

    /// <summary>
    /// Adds a Type to the Derived Type information.
    /// </summary>
    /// <param name="type">Type to add.</param>
    public void Add(Type type) {
        _singleStepDerivedTypes.Add(type);
    }

    /// <summary>
    /// Gets the contained information about types.
    /// </summary>
    /// <returns>IEnumerable of Types contained in this object.</returns>
    public IEnumerable<Type> GetTypes() {
        return _singleStepDerivedTypes;
    }
}

Наконец, давайте посмотрим на ObjectHolder, как яВообразил, чтобы это было.Самое сложное в вашем случае, я думаю, что вы не придерживались принципа единой ответственности и пытались сделать все за один раз.

 public class Objectholder
 {
    /// <summary>
    /// Holds Type vs object information.
    /// Each object is seperated into its own Type.
    /// </summary>
    private readonly ConcurrentDictionary<Type, List<object>> _dict = new ConcurrentDictionary<Type, List<object>>();

    /// <summary>
    /// I already explained about this class before so here I will pass.
    /// </summary>
    private readonly InheritanceInfo inheritanceInfo = new InheritanceInfo();

    /// <summary>
    /// Adds an object to ObjectHolder.
    /// </summary>
    /// <param name="obj">Object to add</param>
    public void AddObject(object obj) {
        _dict.AddOrUpdate(obj.GetType(), t => AddValueFactory(obj), (t, li) => UpdateValueFactory(obj, li));
    }

    /// <summary>
    /// Gets Objects which are assignable to type of T.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public IEnumerable<T> GetObjectsOf<T>() {
        var tree = inheritanceInfo.GetAssignables(typeof(T)).Concat(new[] { typeof(T) });
        return tree.SelectMany(t => _dict[t]).Cast<T>();
    }

    /// <summary>
    /// Adds a value to dictionary.
    /// </summary>
    /// <param name="obj">Object to add.</param>
    /// <returns></returns>
    private List<object> AddValueFactory(object obj)
    {
        inheritanceInfo.Update(obj.GetType());
        var l = new List<object>();
        l.Add(obj);
        return l;
    }

    /// <summary>
    /// Updates a value in dictionary.
    /// </summary>
    /// <param name="obj">Object to add.</param>
    /// <param name="li">List of objects</param>
    /// <returns></returns>
    private List<object> UpdateValueFactory(object obj, List<object> li)
    {
        inheritanceInfo.Update(obj.GetType());
        li.Add(obj);
        return li;
    }
}

// Mock classes
public class A { }
public class B : A { }
public class C : B { }

Использование ObjectHolder такое же, как вы хотелиделать.

var a = new A();
var b = new B();
var c = new C();
var objectholder = new Objectholder();
objectholder.AddObject(a);
objectholder.AddObject(b);
objectholder.AddObject(c);
// Contains A and B and C
var allA = objectholder.GetObjectsOf<A>().ToArray();
0 голосов
/ 27 января 2019

Необходимо убедиться, что в кэшированном списке содержится объект Assignable типа T.Попробуйте код ниже, чтобы проверить это отношение

public List<object> GetObjectsOfType<T>()
{
   foreach (var pair in objs)
   {
      if (pair.Key == typeof(T) || typeof(T).IsAssignableFrom(pair.Key))
      {
         return pair.Value;
      }
   }

   return new List<object>();
}

Я знаю, что ваш код выше просто для демонстрации случая, но в функции AddObject есть ошибка, вы всегда сохраняете объект, а не список, функция должна выглядеть примерно так:

public void AddObject(object obj)
{
    if (!objs.ContainsKey(obj.GetType()))
    {
        objs[obj.GetType()] = new List<object>() { obj };
    }
    else
    {
        objs[obj.GetType()].Add(obj);
    }
}
0 голосов
/ 27 января 2019

Вы можете использовать метод Type.IsAssignableFrom , чтобы определить, какие классы можно назначить универсальному.

Измените метод GetObjectsOfType<T> на следующее:

return objs.Where(x => typeof(T).IsAssignableFrom(x.Key))
           .Select(x => x.Value)
           .ToList<object>();

А также:

objs[obj.GetType()] = obj;

должно быть:

objs[obj.GetType()].Add(obj);
0 голосов
/ 27 января 2019

Если я правильно понимаю, если B наследует A и вы ищете A, вы также хотите вернуть все объекты B.

Я бы сделал это так: Измените существующий метод AddObject надобавить объект в несколько коллекций.Вы можете использовать свойство Type.BaseType, чтобы получить базовый тип.Делайте это в цикле, пока не достигнете object класса.

public void AddObject(object obj) {
    Type type;
    do {
        type = obj.GetType();
        if (!objs.ContainsKey(type) {
            objs[type] = new List<object>();
        }
        objs[type] = obj;
        if(type == typeof(object)) break;
        type = type.BaseType;
    } while(true);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...