Читая вопрос о concurrency
в коллекции, сделанной другим пользователем, я начал пытаться написать свою собственную threadsafe list class
.
После этого я создал тестовый сценарий, который я пытался использовать, и в результате он выдавал ошибки на foreach
из-за оригинального GetEnumerator
метода, который был:
public IEnumerator<T> GetEnumerator()
{
lock (Lock)
{
return List.GetEnumerator();
}
}
Я написал это снова (прочитав несколько советов в Интернете), таким образом, и это сработало. Я думал, что это просто возвращение копии original collection's enumerator
:
public IEnumerator<T> GetEnumerator()
{
lock (Lock)
{
using (IEnumerator<T> enumeratorCopy = List.GetEnumerator())
{
while (enumeratorCopy.MoveNext())
{
yield return enumeratorCopy.Current;
}
}
}
}
Затем для любопытства я попытался отредактировать свойство данного class
и заметил, что отредактированные значения сохраняются.
Можете ли вы объяснить мне, почему? yield
дает reference
вместо value
?
Это полный код:
class Program
{
private static ThreadSafeList<Dog> Dogs = new ThreadSafeList<Dog>();
static void Main(string[] args)
{
Task.Run(() =>
{
while (true)
{
Dogs.Add(new Dog(Guid.NewGuid().ToString()));
Thread.Sleep(500);
}
});
while (true)
{
Print("{0} - There are {1} Dogs registered.", DateTime.Now, Dogs.Count);
foreach (Dog dog in Dogs)
{
Print("{0} - {1}", DateTime.Now, dog);
dog.EditCreationDateTime(default(DateTime));
}
Thread.Sleep(100);
}
}
private static void Print(string message, params object[] args)
{
Console.WriteLine(string.Format(message, args));
}
}
public class Dog
{
public Guid Guid { get; private set; }
public string Name { get; private set; }
public DateTime CreationDateTime { get; private set; }
public Dog(string name)
{
Guid = Guid.NewGuid();
Name = name;
CreationDateTime = DateTime.Now;
}
public override string ToString()
{
return string.Format("{{ Guid: {0}, Name: {1}, CreationDateTime: {2} }}", Guid, Name, CreationDateTime);
}
public void EditCreationDateTime(DateTime dateTime)
{
CreationDateTime = dateTime;
}
}
public class ThreadSafeList<T> : IEnumerable<T>, IEnumerable
{
private List<T> List { get; set; }
private object Lock { get; set; }
public int Count
{
get
{
lock (Lock)
{
return List.Count;
}
}
}
public ThreadSafeList()
{
List = new List<T>();
Lock = new object();
}
public T this[int index]
{
get
{
lock (Lock)
{
return List[index];
}
}
set
{
lock (Lock)
{
List[index] = value;
}
}
}
public void Add(T obj)
{
lock (Lock)
{
List.Add(obj);
}
}
public IEnumerator<T> GetEnumerator()
{
lock (Lock)
{
using (IEnumerator<T> enumeratorCopy = List.GetEnumerator())
{
while (enumeratorCopy.MoveNext())
{
yield return enumeratorCopy.Current;
}
}
}
}
IEnumerator IEnumerable.GetEnumerator()
{
lock (Lock)
{
using (IEnumerator<T> enumeratorCopy = List.GetEnumerator())
{
while (enumeratorCopy.MoveNext())
{
yield return enumeratorCopy.Current;
}
}
}
}
}