Во-первых, я думаю, что хранение нескольких типов данных, как это, вероятно, плохая идея - альтернативы см. Ниже. C # строго типизирован (по большей части) и работает лучше всего, когда знает заранее, что происходит с типами. Но у него есть способы определения того, какой тип на самом деле хранится в object
, и позволяет вам возвращаться к соответствующим типизированным ссылкам.
В этом случае лучше всего подходит для вашей проблемы оператор is
. Это позволит вам проверить, совместим ли object
(или ссылка, или ...) с указанным типом:
object val = task.Data["StartDate"];
if (val is DateTimeOffset)
{
var dto = (DateTimeOffset)val;
// do something with the DateTimeOffset
}
В C # версии 7 они представили вариант сопоставления с образцом, который позволяет сокращать преобразование типов:
object val = task.Data["StartDate"];
if (val is DateTimeOffset dto)
{
// do something
}
Это работает как для ссылочных типов, так и для типов значений, и будет проходить любой действительный неконвертирующий тип преобразования. Это также удобно для тестирования интерфейсов типов в общем коде:
if (someobject is IDisposable disp)
{
disp.Dispose();
}
В общем, я бы по возможности избегал вашего словаря объектов. Хотя есть некоторые случаи, когда это действительно, эти случаи довольно редки. Например, ViewBag
в ASP.NET MVC - один из случаев, когда он работает, но есть и другие способы, которые можно было бы сделать.
Причина проста: для упаковки и распаковки значений требуется время . Тестирование типов значений требует времени, которое вам больше не нужно тратить, если вы используете статические типы. И вам не нужно беспокоиться о том, что кто-то вставит строку, где вы ожидаете int.
Для вашего собственного кода более нормальным случаем было бы определить класс для хранения данных задачи и передать это:
public class TaskData
{
public int StoreID { get; set; }
public DateTimeOffset StartDate { get; set; }
}
... и использовать его как:
task.Data = new TaskData { StoreID = storeID, startDate = DateTimeOffset.Now.AddDays(3) };
Теперь ваша задача будет знать, с чем имеет дело напрямую, не требуется кастинг. И нет никаких шансов, что кто-то придет и назначит какое-то дурацкое значение, с которым ваш код не знает, как обращаться с одной из ваших переменных. Или забудьте о DateTimeOffset
и используйте DateTime
вместо этого, без каких-либо жалоб со стороны компилятора.
Если вы действительно не хотите определять все виды классов только для такого рода вещей, вы можете использовать типы C # 7 Tuple , чтобы сделать нечто подобное:
// in your task class:
public (int StoreID, DateTimeOffset StartDate) Data { get; set; }
// initialize in other code:
task.Data = (storeID, DateTimeOffset.Now.AddDays(3));
В приведенном выше типе определяется во время компиляции с украшениями для имен и так, чтобы вы могли использовать их в других местах вашего кода, но они по сути являются синтаксическим сахаром, используемым компилятором, и недоступны во время выполнения посредством отражения и т. Д. Думайте об этом как о удобном сокращении для упрощения определенных вещей.
Конечно, если вам действительно необходимо использовать Dictionary<string, object>
для этих данных, тогда продолжайте. Просто спросите себя, стоит ли накладные расходы.