Изменить значение словаря как выходной параметр и ссылку
Я хочу создать пример Entity-Component-System. У меня есть такие компоненты, как
internal struct Position : IComponent
{
public int X { get; set; }
public int Y { get; set; }
}
а также
internal struct Movementspeed : IComponent
{
public float Value { get; set; }
}
которые реализуют
internal interface IComponent
{
}
Проходя по компонентам, я хочу найти их как можно быстрее. Я думал о создании словаря, который принимает тип компонентов в качестве ключа и компонент в качестве значения.
internal class ComponentCollection
{
public ComponentCollection()
{
components = new Dictionary<Type, object>();
}
private Dictionary<Type, object> components;
public void AddComponent<T>(T component) // add a new component
{
Type componentType = typeof(T);
components.Add(componentType, component as IComponent);
}
public void RemoveComponent(Type componentType) // remove the component by type
{
components.Remove(componentType);
}
public bool TryGetComponent<T>(out T component) // get the specified component
{
try
{
Type componentType = typeof(T);
component = (T)components[componentType];
return true;
}
catch (Exception)
{
component = default(T);
return false;
}
}
}
Есть две проблемы, которые возникают.
Что если кто-то создаст новый MovementspeedComponent, используя
float movementspeed
и пытается это добавить? Наличие двух значений с плавающей точкой приведет к исключению дубликата ключа. Я мог бы только добавить пользовательские компоненты структуры, которые реализуютIComponent
интерфейс для предотвращения дубликатов.Когда я пытаюсь изменить компоненты, они не изменены по ссылке
private static void Main(string[] args) { ComponentCollection components = new ComponentCollection(); components.AddComponent(new Position()); components.AddComponent(new Movementspeed()); if (components.TryGetComponent(out Position fooPosition)) { fooPosition.X++; Console.WriteLine(fooPosition.X); // prints 1 } if (components.TryGetComponent(out Position barPosition)) { Console.WriteLine(barPosition.X); // prints 0 } Console.ReadLine(); }
Какие-либо предложения?
2 ответа
Что если кто-то создаст новый MovementspeedComponent, используя float движения peed
Типы хороши для ключей, когда у нас есть один объект / коллекция для каждого типа, в противном случае Dictionary<Type, object>
не подходит, ключ должен быть уникальным.
Когда я пытаюсь изменить компоненты, они не изменены по ссылке
Поскольку все компоненты struct
С этой строкой кода:
components.TryGetComponent(out Position fooPosition)
Получает копию структуры, которую мы имеем в словаре. Цитата из руководства C#:
Структуры копируются по назначению. Когда структура присваивается новой переменной, все данные копируются, и любое изменение новой копии не изменяет данные для исходной копии. Это важно помнить при работе с коллекциями типов значений, таких как Словарь.
Что если кто-то создаст новый MovementspeedComponent, используя float движения peed, и попытается добавить его?
Я вижу 2 решения. Либо используйте классы вместо структур, либо добавьте метод Update для обновления структуры в словаре
public void UpdateComponent<T>(T component)
{
Type componentType = typeof(T);
components[componentType] = component;
}
Затем вызовите этот метод после обновления структуры:
if (components.TryGetComponent(out Position fooPosition))
{
fooPosition.X++;
components.UpdateComponent(fooPosition);
Console.WriteLine(fooPosition.X); // prints 1
}
Если вы будете использовать классы вместо структур, ваше старое решение должно работать.