问题
I'm trying to write a class that will be in charge of persisting application options. Since the options need to be persisted the values that I'm sent must be serialisable.
Initially I thought I've be able to write a method with a signature like this:
Public Sub SaveOption(Of T As ISerializable)(ByVal id As String, ByVal value As T)
or if you prefer C#:
public void SaveOption<T>(string id, T value) where T : ISerializable
In principle this would be fine, but what about types that have the <Serializable>
attribute? The most notable example of this is System.String, it does not implement ISerializable
, but clearly it is a type that I should be able to save.
So, is there a way that I can restrict which types are allowed into a method at compiletime, based on their attributes?
回答1:
You could have overloads for the other types - taking your string example:
public void SaveOption(string id, string value)
However; serializability is... tricky; I expect you're going to have to check this at runtime.
回答2:
An attribute constraint wouldn't achieve much, since an attribute doesn't generally offer any guarantees at compile time, they're information for the runtime rather than the compiler.
In the serialization case, the [Serializable]
attribute is only an indicator that you can attempt serialization at runtime, while ISerializable
guarantees that you can serialize it, because you can definitely call GetObjectData
and it's the class's problem to ensure that that does the right thing.
For example, I can have
[Serializable]
class GoodClass
{
public object Property { get; set; }
}
class BadClass
{
}
but GoodClass
is really no better than BadClass
, because I can do
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
EDIT: Attributes also aren't inherited, so the compiler can't be sure that the object passed at runtime will still have the attribute you demanded:
class NotSoGoodClass : GoodClass // No [Serializable] attribute
{
}
...
SaveOption<GoodClass>( "id", new NotSoGoodClass() ) // oops
回答3:
Nope. :(
Ah, no, actually in C# 4.0 they've introduced code contracts. That should work here.
Example from this link: 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
…
}
回答4:
It is possible to check for this, but you're right, it will have to be done at runtime, but more formally than just throwing an exception.
public static byte[] SerializeObject<T>(this T obj)
{
Contract.Requires(typeof(T).IsSerializable);
...
}
来源:https://stackoverflow.com/questions/1346485/is-it-possible-to-use-generic-constraints-based-on-attributes-rather-than-types