首先,我认为像这样存储多种类型的数据可能是一个坏主意 - 请参阅下面的替代方案。 C# 是强类型的(在大多数情况下),并且当它提前知道类型发生了什么时效果最好。但它确实有办法确定 object 中实际存储的类型,并让您转换回正确的类型引用。
在这种情况下,最适合您的问题的似乎是is operator。这将让您测试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();
}
一般来说,尽管我会尽可能避免使用您的对象字典。虽然在某些情况下它是有效的,但这些情况相当罕见。例如,ASP.NET MVC 中的 ViewBag 是一种可行的情况,但还有其他方法可以做到。
原因很简单:boxing and unboxing values takes time。测试值的类型需要时间,如果您使用静态类型,您通常不需要浪费这些时间。而且您不必担心有人将字符串粘贴到您期望 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>,那么请继续。问问自己这些开销是否值得付出努力。