我正在尝试编写一个负责持久应用程序选项的类。由于需要保留选项,因此我发送的值必须是可序列化的。
最初我以为我能用这样的签名编写一个方法:
Public Sub SaveOption(Of T As ISerializable)(ByVal id As String, ByVal value As T)
或者如果您更喜欢C#:
public void SaveOption<T>(string id, T value) where T : ISerializable
原则上这可以,但是具有<Serializable>
属性的类型呢?最值得注意的例子是System.String,它没有实现ISerializable
,但显然它是一种我应该能够保存的类型。
那么,有没有一种方法可以根据属性限制哪些类型在编译时被允许进入方法?
答案 0 :(得分:2)
你可能有其他类型的重载 - 以你的字符串为例:
public void SaveOption(string id, string value)
然而;可串行性是......棘手的;我希望你不得不在运行时检查这个。
答案 1 :(得分:2)
属性约束不会实现太多,因为属性通常在编译时不提供任何保证,它们是运行时而不是编译器的信息。
在序列化的情况下,[Serializable]
属性只是一个指示器,您可以在运行时尝试序列化,而ISerializable
保证您可以序列化它,因为您可以肯定会打电话给GetObjectData
,这是班级的问题,以确保这是正确的。
例如,我可以
[Serializable]
class GoodClass
{
public object Property { get; set; }
}
class BadClass
{
}
但是GoodClass
实际上并不比BadClass
好,因为我可以做到
MemoryStream ms = new MemoryStream();
BinaryFormatter f = new BinaryFormatter();
GoodClass obj = new GoodClass();
f.Serialize(ms, obj); // OK
obj.Property = new BadClass();
f.Serialize(ms, obj); // BOOM
编辑:属性也不会被继承,因此编译器无法确定在运行时传递的对象是否仍具有您需要的属性:
class NotSoGoodClass : GoodClass // No [Serializable] attribute
{
}
...
SaveOption<GoodClass>( "id", new NotSoGoodClass() ) // oops
答案 2 :(得分:1)
不。 :(
啊,不,实际上在C#4.0中,他们已经引入了代码契约。这应该在这里工作。
此链接示例:CLR 4.0: Code Contracts
public void BuyMoreStuff(Item[] cart, ref Decimal totalCost, Item i)
{
CodeContract.Requires(totalCost >=0);
CodeContract.Requires(cart != null);
CodeContract.Requires(CodeContract.ForAll(cart, s => s != i));
CodeContract.Ensures(CodeContract.Exists(cart, s => s == i);
CodeContract.Ensures(totalCost >= CodeContract.OldValue(totalCost));
CodeContract.EnsuresOnThrow<IOException>(totalCost == CodeContract.OldValue(totalCost));
// Do some stuff
…
}
答案 3 :(得分:0)
可以检查这个,但是你是对的,它必须在运行时完成,但更正式而不仅仅是抛出异常。
public static byte[] SerializeObject<T>(this T obj)
{
Contract.Requires(typeof(T).IsSerializable);
...
}