Как получить данные из анонимного типа, которые были преобразованы в объект?

Я создаю диспетчер замедленных функций, чтобы вы могли вызвать TimeManager.DelayedCall(uniqueid, delay, action), чтобы сделать action вызывается в delay секунды. Однако у меня проблемы. Здесь код пока.

private static Dictionary<string, object> delays = new Dictionary<string, object>();

public static void Think(float dt)
{
        timestep = dt * timescale;

        time += timestep;

        foreach (KeyValuePair<string, object> kv in delays)
        {
            if (time > kv.Value.ourtime)
            {
                kv.Value.action();
            }
        }
}

public static void DelayedCall(string id, float delay, Action a)
{
        delays[id] = new { ourtime = time + delay, action = a };
}

Этот код не компилируется. Поскольку я преобразовываю анонимный тип из функции DelayedCall в object, я не могу получить переменные ourtime и action от него в функции Think! Кто-нибудь знает, как это исправить, или как это сделать лучше?

Ответ 1

EDIT Исправлены некоторые подробности. См. Комментарий Matti Virkkunen ниже.

У вашего анонимного типа есть только область применения метода, в котором вы его создаете. Вы не можете получить свойства в функции think, потому что после того, как вы добавите его в объект и сохраните его в словаре, у вас нет установленных свойств. вы не можете вернуть его в исходный тип, созданный компилятор, поэтому вы не можете получить доступ к свойствам, которые вы устанавливаете, кроме как косвенно через отражение.

Зачем вам вообще нужно использовать анонимные типы и тип объекта?

На основе отправленного кода я бы просто создал объект DelayData, который имеет все свойства, необходимые для установки, создания и хранения экземпляров DelayData в словаре, а затем использовать этот объект непосредственно из словаря.

Ответ 2

Ну... Вы всегда можете использовать отражение для доступа к полям в своем объекте, но это медленное и потенциально уродливое (хотя эта последняя точка субъективна). Более чистая версия будет включать динамические объекты (но тогда вы потеряете безопасность типов).

Но почему вы должны использовать анонимный тип? Не можете ли вы написать небольшую обертку class или struct, а затем использовать это вместо object в словаре?

Ответ 3

Вы можете использовать отражение:

Action a = () => { Console.Write("Hello"); };
object o = new { ourtime = DateTime.Now, action = a };

DateTime ourtime = (DateTime) o.GetType().GetProperties()[0].GetValue(o, null);
Action action = (Action) o.GetType().GetProperties()[1].GetValue(o, null) ;

Но было бы намного проще создать новый тип для хранения этих значений, т.е. TimedAction с двумя свойствами Time и Action

Ответ 4

Я бы избавился от AnonymousType. Это просто делает вещи неясными и возможность закрепить значения в словаре, добавив неправильный тип. Я также добавил некоторые Linq и использование List.ForEach.

public class Thing
{
    public double ourtime;
    public Action action;
}

public class Program
{
    private readonly Dictionary<string, Thing> delays = new Dictionary<string, Thing>();

    public void Think(float dt)
    {
        timestep = dt * timescale;

        time += timestep;

        var actions = delays.Where(d => time > d.Value.ourtime).Select(d => d.Value.action).ToList();
        actions.ForEach(a => a());
    }

    public void DelayedCall(string id, float delay, Action a)
    {
        delays[id] = new Thing { ourtime = time + delay, action = a };
    }

Ответ 5

Я бы написал крошечный класс с двумя свойствами и использовал это вместо анонимного класса. Другим вариантом является переход на динамические объекты и отказ от безопасности типа.

http://msdn.microsoft.com/en-us/library/dd264736.aspx

Ответ 6

Почему бы не создать новый тип

class DelayAction
{
    float OurTime;
    Action a;
}

и измените на

private static Dictionary<string, DelayAction> delays = new Dictionary<string, DelayAction>();

и

public static void DelayedCall (идентификатор строки, задержка с плавающей точкой, действие a) {       delayes [id] = new DelayAction {ourtime = time + delay, action = a}; }

Ответ 7

Я делаю метод расширения ToDictionary

object anonymous = new
{
    Id = 1234,
    Password = "pwd",
};

var dictionary = anonymous.ToDictionary();

и метод расширения был ниже.

    public static IDictionary<string, dynamic> ToDictionary<T>(this T target, bool exceptDefaultValue = false)
        where T : class
    {
        if (target == null)
        {
            throw new ArgumentNullException("target");
        }

        IDictionary<string, dynamic> dictionary = new Dictionary<string, dynamic>(System.StringComparer.OrdinalIgnoreCase);

        foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(target))
        {
            object value = propertyDescriptor.GetValue(target);

            if (exceptDefaultValue == true)
            {
                if (value == null || value.Equals(propertyDescriptor.PropertyType.DefaultValue()) == true)
                {
                    continue;
                }
            }

            dictionary.Add(propertyDescriptor.Name, value);
        }

        return dictionary;
    }