изменить значение словаря как выходной параметр и ссылку - PullRequest
0 голосов
/ 10 декабря 2018

Я хочу создать пример 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;
        }
    }
}

Возникают две проблемы.

  1. Чтоесли кто-то создает новый MovementspeedComponent, используя float movementspeed, и пытается добавить его?Наличие двух чисел с плавающей точкой приведет к исключению дублированного ключаЯ мог бы только добавить пользовательские компоненты структуры, которые реализуют интерфейс IComponent для предотвращения дублирования.

  2. Когда я пытаюсь изменить компоненты, они не изменяются по ссылке

    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 ]

0 голосов
/ 10 декабря 2018

Я вижу 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
}

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

0 голосов
/ 10 декабря 2018

Что если кто-то создаст новый MovementspeedComponent, используя float движенияpeed

Типы хороши для ключей, когда у нас есть один объект / коллекция для каждого типа, иначе Dictionary<Type, object> не годитсяподходит, ключ должен быть уникальным.

Когда я пытаюсь изменить компоненты, они не изменяются по ссылке

Поскольку все компоненты struct s этой строкикод:

components.TryGetComponent(out Position fooPosition)

Получает копию структуры, которую мы имеем в словаре.Цитата из C # guide :

Структуры копируются при назначении.Когда структура присваивается новой переменной, все данные копируются, и любое изменение новой копии не изменяет данные для исходной копии.Это важно помнить при работе с коллекциями типов значений, таких как Словарь.

Что если кто-то создаст новый MovementspeedComponent, используя float движенияpeed, и попытается добавить его?

...