Здравствуйте. Не так давно выполнил (во всяком случае так считал) тестовое задание. Описание задачи:
Спроектируйте и реализуйте класс-коллекцию для хранения элементов, имеющих уникальный составной ключ [Id, Name] (Id и Name – компоненты ключа, могу быть произвольного типа). Вы можете использовать любую встроенную коллекцию .NET Framework.
Предоставьте необходимые с вашей точки зрения методы для этой коллекции, а так же методы для эффективного по скорости получения элементов по их Id ИЛИ Name.
Программа должна содержать пример использования коллекции с идентификатором ключа в виде пользовательского типа (т.е. Id в ключе – UserType, объекты UserType должны сравниваться по значению).
Дополнительно: Создайте потокобезопасную реализацию этой коллекции (не использовать коллекции из пространства имён System.Collections.Concurrent).
То, что я предложил в качестве решения:
Элемент и UserType:
/// <summary>
/// Элемент коллекции
/// </summary>
public class MyElement
{
private UserType id;
private String name;
public String Name
{
get { return name; }
set { name = value; }
}
public UserType Id
{
get { return id; }
set { id = value; }
}
public override int GetHashCode()
{
return Name.GetHashCode() ^ Id.GetHashCode();
}
public override bool Equals(object obj)
{
if (!(obj is MyElement))
{
MyElement el = (MyElement)obj;
return Id == el.Id && Name == el.Name;
}
else
{
return false;
}
}
public override string ToString()
{
return String.Format("Id:{0}, Name:{1}",id.Content.ToString(), Name);
}
}
/// <summary>
/// Тип для поля Id
/// </summary>
public struct UserType
{
private int content;
public int Content
{
get { return content; }
set { content = value; }
}
public override int GetHashCode()
{
return Content^Content;
}
public override bool Equals(object obj)
{
if (!(obj is UserType))
{
UserType ut = (UserType)obj;
return Content == ut.Content;
}
else
{
return false;
}
}
public static Boolean operator == (UserType ut1, UserType ut2)
{
return ut1.Content == ut2.Content;
}
public static Boolean operator != (UserType ut1, UserType ut2)
{
return ut1.Content != ut2.Content;
}
public override string ToString()
{
return Content.ToString();
}
}
Индекс:
/// <summary>
/// Индекс для коллекции
/// </summary>
public struct MyId : IEquatable<MyId>
{
private readonly UserType id;
private readonly String name;
public UserType Id
{
get
{
return id;
}
}
public String Name
{
get
{
return name;
}
}
//public MyId(UserType _id, String _name)
//{
// id = _id;
// name = _name;
//}
public MyId(MyElement element)
{
id = element.Id;
name = element.Name;
}
public override int GetHashCode()
{
return id.GetHashCode() ^ name.GetHashCode();
}
public bool Equals(MyId other)
{
if ((object)other == null)
{
return false;
}
else
{
return id == other.id && name == other.name;
}
}
public override bool Equals(object obj)
{
return Equals((MyId)obj);
}
public static Boolean operator == (MyId u1, MyId u2)
{
return (u1.id == u2.id) && (u1.name == u2.name);
}
public static Boolean operator != (MyId u1, MyId u2)
{
return (u1.id == u2.id) || (u1.name == u2.name);
}
public override string ToString()
{
return String.Format("Key(id={0}, name={1})", id.ToString(), name);
}
}
Собственно, коллекция:
/// <summary>
/// Класс-коллекция. Потокобезопасность реализована для метода Add.
/// Т.к. коллекция наследует от Dictionary, она поддерживает необходимые базовые методы
/// </summary>
/// <typeparam name="TKey">В качестве ключа используется MyId</typeparam>
/// <typeparam name="TValue">Элемент коллекции</typeparam>
public class MyCollection<TKey, TValue> : Dictionary<MyId, TValue>
{
private readonly object syncRoot = new object();
private Dictionary<MyId, TValue> _myCollection = new Dictionary<MyId, TValue>();
/// <summary>
/// Перекрываем метод вставки в коллекцию для реализации потокобезопасности.
/// Примечание: при попытка вставить элемент с повторяющимся индексом просто игнорируем вставку.
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public new void Add(MyId key, TValue value)
{
lock (syncRoot)
{
if (!this.ContainsKey(key))
{
base.Add(key, value);
}
}
}
#region Реализация методов поиска указанных в задании
public MyElement GetElementById(UserType _id)
{
dynamic result;
lock (syncRoot)
{
result = this.Where(x => x.Key.Id == _id).SingleOrDefault().Value;
}
if (result is MyElement)
{
return result as MyElement;
}
else
{
return null;
}
}
public MyElement GetElementByName(String _name)
{
dynamic result;
lock (syncRoot)
{
result = this.Where(x => x.Key.Name == _name).SingleOrDefault().Value;
}
if (result is MyElement)
{
return result as MyElement;
}
else
{
return null;
}
}
public MyElement GetElementByIdOrName(object o)
{
if (o is String)
{
return GetElementByName((string)o);
}
else if (o is UserType)
{
return GetElementById((UserType)o);
}
else
{
return null;
}
}
#endregion
}
Этот вариант решения, как видно, не понравился проверявшему.
Буду признателен, если "ткнете носом" в ошибки. Понимаю, что, возможно подошел к решению довольно формально, но по требованиям вроде все реализовал. Или чего-то не так понял.